From 363e4e98cf04d40a36626fdc6237bddc00d9b0b6 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Wed, 22 May 2024 13:00:53 +0300 Subject: [PATCH 01/12] chore: add automatic approach nav 1 --- example/lib/main.dart | 3 +- lib/instabug_flutter.dart | 1 + lib/src/modules/apm.dart | 8 +++ .../utils/screen_loading/route_wrapper.dart | 43 ++++++++++++++ .../screen_loading_manager.dart | 26 ++++++++ .../screen_loading/route_wrapper_test.dart | 59 +++++++++++++++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 lib/src/utils/screen_loading/route_wrapper.dart create mode 100644 test/utils/screen_loading/route_wrapper_test.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 8728b0bb6..4fc56f1fd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,6 +12,7 @@ import 'src/widget/instabug_button.dart'; import 'src/widget/instabug_clipboard_input.dart'; import 'src/widget/instabug_text_field.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; +import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; void main() { runZonedGuarded( @@ -45,7 +46,7 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + home: const RouteWrapper(child: MyHomePage(title: 'Flutter Demo Home Page') , routes: {},), ); } } diff --git a/lib/instabug_flutter.dart b/lib/instabug_flutter.dart index 585858093..cfd50ce30 100644 --- a/lib/instabug_flutter.dart +++ b/lib/instabug_flutter.dart @@ -17,3 +17,4 @@ export 'src/modules/surveys.dart'; // Utils export 'src/utils/instabug_navigator_observer.dart'; export 'src/utils/screen_loading/instabug_capture_screen_loading.dart'; +export 'src/utils/screen_loading/route_matcher.dart'; diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index 5c46bd7f6..f5c73a707 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -2,6 +2,7 @@ import 'dart:async'; +import 'package:flutter/widgets.dart' show WidgetBuilder; import 'package:instabug_flutter/src/generated/apm.api.g.dart'; import 'package:instabug_flutter/src/models/network_data.dart'; import 'package:instabug_flutter/src/models/trace.dart'; @@ -217,4 +218,11 @@ class APM { static Future endScreenLoading() { return ScreenLoadingManager.I.endScreenLoading(); } + + /// Wraps the given routes with [InstabugCaptureScreenLoading] widgets. + /// This allows Instabug to automatically capture screen loading times. + static Map wrapRoutes( + Map routes) { + return ScreenLoadingManager.wrapRoutes(routes); + } } diff --git a/lib/src/utils/screen_loading/route_wrapper.dart b/lib/src/utils/screen_loading/route_wrapper.dart new file mode 100644 index 000000000..bf75f455b --- /dev/null +++ b/lib/src/utils/screen_loading/route_wrapper.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; + +/// A widget that wraps the app's routes with [InstabugCaptureScreenLoading] widgets. +/// +/// This allows Instabug to automatically capture screen loading times. +class RouteWrapper extends StatelessWidget { + /// The child widget to wrap. + final Widget child; + + /// A map of routes to wrap. + final Map routes; + + /// The initial route to navigate to. + final String? initialRoute; + + /// Creates a new instance of [RouteWrapper]. + const RouteWrapper( + {Key? key, required this.child, required this.routes, this.initialRoute}) + : super(key: key); + + @override + Widget build(BuildContext context) { + + return Navigator( + // observers: [InstabugNavigatorObserver()], + initialRoute: initialRoute, + onGenerateRoute: (settings) { + final route = routes[settings.name]; + + if (route == null) return null; //Guard case + + return MaterialPageRoute( + builder: (context) => InstabugCaptureScreenLoading( + screenName: settings.name ?? "", + child: route.call(context), + ), + settings: settings, + ); + }, + ); + } +} diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/lib/src/utils/screen_loading/screen_loading_manager.dart index 5c16f5dd9..a04dae97b 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -1,3 +1,4 @@ +import 'package:flutter/widgets.dart' show WidgetBuilder, BuildContext; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; import 'package:instabug_flutter/src/utils/ibg_date_time.dart'; @@ -334,6 +335,31 @@ class ScreenLoadingManager { _logExceptionErrorAndStackTrace(error, stackTrace); } } + + /// Wraps the given routes with [InstabugCaptureScreenLoading] widgets. + /// + /// This allows Instabug to automatically capture screen loading times. + /// + /// Example usage: + /// + /// Map routes = { + /// '/home': (context) => const HomePage(), + /// '/settings': (context) => const SettingsPage(), + /// }; + /// + /// Map wrappedRoutes = + /// ScreenLoadingAutomaticManager.wrapRoutes( routes) + static Map wrapRoutes( + Map routes, + ) { + return { + for (final entry in routes.entries) + entry.key: (BuildContext context) => InstabugCaptureScreenLoading( + screenName: entry.key, + child: entry.value(context), + ), + }; + } } class DropScreenLoadingError extends Error { diff --git a/test/utils/screen_loading/route_wrapper_test.dart b/test/utils/screen_loading/route_wrapper_test.dart new file mode 100644 index 000000000..501348f07 --- /dev/null +++ b/test/utils/screen_loading/route_wrapper_test.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; +import 'package:mockito/annotations.dart'; + +import 'route_wrapper_test.mocks.dart'; + + +@GenerateMocks([Placeholder]) +void main() { + + late MockPlaceholder mockWidget; + setUp(() => mockWidget = MockPlaceholder()); + + group('RouteWrapper', () { + testWidgets('wraps routes with InstabugCaptureScreenLoading widgets', + (WidgetTester tester) async { + // Create a map of routes + final routes = { + '/home': (context) => mockWidget, + '/settings': (context) => mockWidget, + }; + + // Create a RouteWrapper widget + final routeWrapper = RouteWrapper( + routes: routes, + child: const MaterialApp(), + ); + + // Pump the widget into the tester + await tester.pumpWidget(routeWrapper); + + // Verify that the routes are wrapped with InstabugCaptureScreenLoading widgets + expect(find.byType(InstabugCaptureScreenLoading), findsWidgets); + }); + + testWidgets('initializes the initial route', (WidgetTester tester) async { + // Create a map of routes + final routes = { + '/home': (context) => mockWidget, + '/settings': (context) => mockWidget, + }; + + // Create a RouteWrapper widget with an initial route + final routeWrapper = RouteWrapper( + routes: routes, + initialRoute: '/settings', + child: const MaterialApp(), + ); + + // Pump the widget into the tester + await tester.pumpWidget(routeWrapper); + + // Verify that the initial route is set correctly + expect(find.byType(MockPlaceholder), findsOneWidget); + }); + }); +} \ No newline at end of file From 7760240195eed6daee048a353fb0fc3f0458b983 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Thu, 23 May 2024 12:47:43 +0300 Subject: [PATCH 02/12] feat: add SCL automatic approach for Navigator1, refactor: sample app by moving widgets in main.dart to separate files --- example/lib/main.dart | 1368 +---------------- example/lib/src/app_routes.dart | 18 + .../src/components/fatal_crashes_content.dart | 75 + example/lib/src/components/flows_content.dart | 151 ++ .../lib/src/components/network_content.dart | 47 + .../components/non_fatal_crashes_content.dart | 66 + example/lib/src/components/page.dart | 35 + .../lib/src/components/traces_content.dart | 157 ++ example/lib/src/screens/apm_page.dart | 50 + example/lib/src/screens/complex_page.dart | 141 ++ example/lib/src/screens/crashes_page.dart | 22 + example/lib/src/screens/my_home_page.dart | 332 ++++ ...reen_capture_premature_extension_page.dart | 30 + .../lib/src/screens/screen_loading_page.dart | 186 +++ example/lib/src/widget/nested_view.dart | 45 + example/lib/src/widget/section_title.dart | 20 + lib/src/modules/apm.dart | 9 +- .../utils/screen_loading/route_wrapper.dart | 25 +- .../screen_loading_manager.dart | 32 +- .../screen_loading/route_wrapper_test.dart | 118 +- 20 files changed, 1515 insertions(+), 1412 deletions(-) create mode 100644 example/lib/src/app_routes.dart create mode 100644 example/lib/src/components/fatal_crashes_content.dart create mode 100644 example/lib/src/components/flows_content.dart create mode 100644 example/lib/src/components/network_content.dart create mode 100644 example/lib/src/components/non_fatal_crashes_content.dart create mode 100644 example/lib/src/components/page.dart create mode 100644 example/lib/src/components/traces_content.dart create mode 100644 example/lib/src/screens/apm_page.dart create mode 100644 example/lib/src/screens/complex_page.dart create mode 100644 example/lib/src/screens/crashes_page.dart create mode 100644 example/lib/src/screens/my_home_page.dart create mode 100644 example/lib/src/screens/screen_capture_premature_extension_page.dart create mode 100644 example/lib/src/screens/screen_loading_page.dart create mode 100644 example/lib/src/widget/nested_view.dart create mode 100644 example/lib/src/widget/section_title.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 4fc56f1fd..10f4fb4ec 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,13 +6,40 @@ import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter_example/src/app_routes.dart'; +import 'package:instabug_flutter_example/src/widget/nested_view.dart'; import 'src/native/instabug_flutter_example_method_channel.dart'; import 'src/widget/instabug_button.dart'; import 'src/widget/instabug_clipboard_input.dart'; import 'src/widget/instabug_text_field.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; -import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; + +import 'src/widget/section_title.dart'; + +part 'src/screens/crashes_page.dart'; + +part 'src/screens/complex_page.dart'; + +part 'src/screens/apm_page.dart'; + +part 'src/screens/screen_capture_premature_extension_page.dart'; + +part 'src/screens/screen_loading_page.dart'; + +part 'src/screens/my_home_page.dart'; + +part 'src/components/fatal_crashes_content.dart'; + +part 'src/components/non_fatal_crashes_content.dart'; + +part 'src/components/network_content.dart'; + +part 'src/components/page.dart'; + +part 'src/components/traces_content.dart'; + +part 'src/components/flows_content.dart'; void main() { runZonedGuarded( @@ -22,19 +49,22 @@ void main() { Instabug.init( token: 'ed6f659591566da19b67857e1b9d40ab', invocationEvents: [InvocationEvent.floatingButton], + debugLogsLevel: LogLevel.verbose, ); FlutterError.onError = (FlutterErrorDetails details) { Zone.current.handleUncaughtError(details.exception, details.stack!); }; - runApp(MyApp()); + runApp(const MyApp()); }, CrashReporting.reportCrash, ); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -42,1342 +72,12 @@ class MyApp extends StatelessWidget { navigatorObservers: [ InstabugNavigatorObserver(), ], + routes: APM.wrapRoutes(appRoutes , exclude: [CrashesPage.screenName]), theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const RouteWrapper(child: MyHomePage(title: 'Flutter Demo Home Page') , routes: {},), - ); - } -} - -class SectionTitle extends StatelessWidget { - String text; - - SectionTitle(this.text); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.centerLeft, - margin: const EdgeInsets.only(top: 20.0, left: 20.0), - child: Text( - text, - textAlign: TextAlign.left, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ); - } -} - -class Page extends StatelessWidget { - final String title; - final GlobalKey? scaffoldKey; - final List children; - final Widget? floatingActionButton; - final FloatingActionButtonLocation? floatingActionButtonLocation; - - const Page({ - Key? key, - required this.title, - this.scaffoldKey, - this.floatingActionButton, - this.floatingActionButtonLocation, - required this.children, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - key: scaffoldKey, - appBar: AppBar(title: Text(title)), - body: SingleChildScrollView( - physics: const ClampingScrollPhysics(), - padding: const EdgeInsets.only(top: 20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: children, - )), - floatingActionButton: floatingActionButton, - floatingActionButtonLocation: floatingActionButtonLocation, - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - final buttonStyle = ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.lightBlue), - foregroundColor: MaterialStateProperty.all(Colors.white), - ); - - List reportTypes = []; - - final primaryColorController = TextEditingController(); - final screenNameController = TextEditingController(); - - void restartInstabug() { - Instabug.setEnabled(false); - Instabug.setEnabled(true); - BugReporting.setInvocationEvents([InvocationEvent.floatingButton]); - } - - void setOnDismissCallback() { - BugReporting.setOnDismissCallback((dismissType, reportType) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('On Dismiss'), - content: Text( - 'onDismiss callback called with $dismissType and $reportType', - ), - ); - }, - ); - }); - } - - void show() { - Instabug.show(); - } - - void reportScreenChange() { - Instabug.reportScreenChange(screenNameController.text); - } - - void sendBugReport() { - BugReporting.show(ReportType.bug, [InvocationOption.emailFieldOptional]); - } - - void sendFeedback() { - BugReporting.show( - ReportType.feedback, [InvocationOption.emailFieldOptional]); - } - - void showNpsSurvey() { - Surveys.showSurvey('pcV_mE2ttqHxT1iqvBxL0w'); - } - - void showManualSurvey() { - Surveys.showSurvey('PMqUZXqarkOR2yGKiENB4w'); - } - - final _scaffoldKey = GlobalKey(); - - void getCurrentSessionReplaylink() async { - final result = await SessionReplay.getSessionReplayLink(); - if (result == null) { - const snackBar = SnackBar( - content: Text('No Link Found'), - ); - ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(snackBar); - } else { - var snackBar = SnackBar( - content: Text(result), - ); - ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(snackBar); - } - } - - void showFeatureRequests() { - FeatureRequests.show(); - } - - void toggleReportType(ReportType reportType) { - if (reportTypes.contains(reportType)) { - reportTypes.remove(reportType); - } else { - reportTypes.add(reportType); - } - BugReporting.setReportTypes(reportTypes); - } - - void changeFloatingButtonEdge() { - BugReporting.setFloatingButtonEdge(FloatingButtonEdge.left, 200); - } - - void setInvocationEvent(InvocationEvent invocationEvent) { - BugReporting.setInvocationEvents([invocationEvent]); - } - - void changePrimaryColor() { - String text = 'FF' + primaryColorController.text.replaceAll('#', ''); - Color color = Color(int.parse(text, radix: 16)); - Instabug.setPrimaryColor(color); - } - - void setColorTheme(ColorTheme colorTheme) { - Instabug.setColorTheme(colorTheme); - } - - void _navigateToCrashes() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const CrashesPage(), - settings: const RouteSettings(name: CrashesPage.screenName), - ), - ); - } - - void _navigateToApm() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const InstabugCaptureScreenLoading( - screenName: ApmPage.screenName, - child: ApmPage(), - ), - settings: const RouteSettings(name: ApmPage.screenName), - ), - ); - } - - void _navigateToComplex() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ComplexPage(), - settings: const RouteSettings(name: ComplexPage.screenName), - ), - ); - } - - @override - Widget build(BuildContext context) { - return Page( - scaffoldKey: _scaffoldKey, - title: widget.title, - children: [ - Container( - margin: const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0), - child: const Text( - 'Hello Instabug\'s awesome user! The purpose of this application is to show you the different options for customizing the SDK and how easy it is to integrate it to your existing app', - textAlign: TextAlign.center, - ), - ), - InstabugButton( - onPressed: restartInstabug, - text: 'Restart Instabug', - ), - SectionTitle('Primary Color'), - InstabugTextField( - controller: primaryColorController, - label: 'Enter primary color', - ), - InstabugButton( - text: 'Change Primary Color', - onPressed: changePrimaryColor, - ), - SectionTitle('Change Invocation Event'), - ButtonBar( - mainAxisSize: MainAxisSize.min, - alignment: MainAxisAlignment.start, - children: [ - ElevatedButton( - onPressed: () => setInvocationEvent(InvocationEvent.none), - style: buttonStyle, - child: const Text('None'), - ), - ElevatedButton( - onPressed: () => setInvocationEvent(InvocationEvent.shake), - style: buttonStyle, - child: const Text('Shake'), - ), - ElevatedButton( - onPressed: () => setInvocationEvent(InvocationEvent.screenshot), - style: buttonStyle, - child: const Text('Screenshot'), - ), - ], - ), - ButtonBar( - mainAxisSize: MainAxisSize.min, - alignment: MainAxisAlignment.start, - children: [ - ElevatedButton( - onPressed: () => - setInvocationEvent(InvocationEvent.floatingButton), - style: buttonStyle, - child: const Text('Floating Button'), - ), - ElevatedButton( - onPressed: () => - setInvocationEvent(InvocationEvent.twoFingersSwipeLeft), - style: buttonStyle, - child: const Text('Two Fingers Swipe Left'), - ), - ], - ), - InstabugButton( - onPressed: show, - text: 'Invoke', - ), - InstabugButton( - onPressed: setOnDismissCallback, - text: 'Set On Dismiss Callback', - ), - SectionTitle('Repro Steps'), - InstabugTextField( - controller: screenNameController, - label: 'Enter screen name', - ), - InstabugButton( - text: 'Report Screen Change', - onPressed: reportScreenChange, - ), - InstabugButton( - onPressed: sendBugReport, - text: 'Send Bug Report', - ), - InstabugButton( - onPressed: showManualSurvey, - text: 'Show Manual Survey', - ), - SectionTitle('Change Report Types'), - ButtonBar( - mainAxisSize: MainAxisSize.min, - alignment: MainAxisAlignment.start, - children: [ - ElevatedButton( - onPressed: () => toggleReportType(ReportType.bug), - style: buttonStyle, - child: const Text('Bug'), - ), - ElevatedButton( - onPressed: () => toggleReportType(ReportType.feedback), - style: buttonStyle, - child: const Text('Feedback'), - ), - ElevatedButton( - onPressed: () => toggleReportType(ReportType.question), - style: buttonStyle, - child: const Text('Question'), - ), - ], - ), - InstabugButton( - onPressed: changeFloatingButtonEdge, - text: 'Move Floating Button to Left', - ), - InstabugButton( - onPressed: sendFeedback, - text: 'Send Feedback', - ), - InstabugButton( - onPressed: showNpsSurvey, - text: 'Show NPS Survey', - ), - InstabugButton( - onPressed: showManualSurvey, - text: 'Show Multiple Questions Survey', - ), - InstabugButton( - onPressed: showFeatureRequests, - text: 'Show Feature Requests', - ), - InstabugButton( - onPressed: _navigateToCrashes, - text: 'Crashes', - ), - InstabugButton( - onPressed: _navigateToApm, - text: 'APM', - ), - InstabugButton( - onPressed: _navigateToComplex, - text: 'Complex', - ), - SectionTitle('Sessions Replay'), - InstabugButton( - onPressed: getCurrentSessionReplaylink, - text: 'Get current session replay link', - ), - SectionTitle('Color Theme'), - ButtonBar( - mainAxisSize: MainAxisSize.max, - alignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => setColorTheme(ColorTheme.light), - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.white), - foregroundColor: MaterialStateProperty.all(Colors.lightBlue), - ), - child: const Text('Light'), - ), - ElevatedButton( - onPressed: () => setColorTheme(ColorTheme.dark), - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all(Colors.black), - foregroundColor: MaterialStateProperty.all(Colors.white), - ), - child: const Text('Dark'), - ), - ], - ), - ], - ); - } -} - -class CrashesPage extends StatelessWidget { - static const screenName = 'crashes'; - const CrashesPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Page( - title: 'Crashes', - children: [ - SectionTitle('Non-Fatal Crashes'), - const NonFatalCrashesContent(), - SectionTitle('Fatal Crashes'), - const Text('Fatal Crashes can only be tested in release mode'), - const Text('Most of these buttons will crash the application'), - const FatalCrashesContent(), - ], - ); - } -} - -class NonFatalCrashesContent extends StatelessWidget { - const NonFatalCrashesContent({Key? key}) : super(key: key); - - void throwHandledException(dynamic error) { - try { - if (error is! Error) { - const String appName = 'Flutter Test App'; - final errorMessage = error?.toString() ?? 'Unknown Error'; - error = Exception('Handled Error: $errorMessage from $appName'); - } - throw error; - } catch (err) { - if (err is Error) { - log('throwHandledException: Crash report for ${err.runtimeType} is Sent!', - name: 'NonFatalCrashesWidget'); - } - } - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - InstabugButton( - text: 'Throw Exception', - onPressed: () => - throwHandledException(Exception('This is a generic exception.')), - ), - InstabugButton( - text: 'Throw StateError', - onPressed: () => - throwHandledException(StateError('This is a StateError.')), - ), - InstabugButton( - text: 'Throw ArgumentError', - onPressed: () => - throwHandledException(ArgumentError('This is an ArgumentError.')), - ), - InstabugButton( - text: 'Throw RangeError', - onPressed: () => throwHandledException( - RangeError.range(5, 0, 3, 'Index out of range')), - ), - InstabugButton( - text: 'Throw FormatException', - onPressed: () => - throwHandledException(UnsupportedError('Invalid format.')), - ), - InstabugButton( - text: 'Throw NoSuchMethodError', - onPressed: () { - dynamic obj; - throwHandledException(obj.methodThatDoesNotExist()); - }, - ), - InstabugButton( - text: 'Throw Handled Native Exception', - onPressed: - InstabugFlutterExampleMethodChannel.sendNativeNonFatalCrash, - ), - ], - ); - } -} - -class FatalCrashesContent extends StatelessWidget { - const FatalCrashesContent({Key? key}) : super(key: key); - - void throwUnhandledException(dynamic error) { - if (error is! Error) { - const String appName = 'Flutter Test App'; - final errorMessage = error?.toString() ?? 'Unknown Error'; - error = Exception('Unhandled Error: $errorMessage from $appName'); - } - throw error; - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - InstabugButton( - text: 'Throw Exception', - onPressed: () => throwUnhandledException( - Exception('This is a generic exception.')), - ), - InstabugButton( - text: 'Throw StateError', - onPressed: () => - throwUnhandledException(StateError('This is a StateError.')), - ), - InstabugButton( - text: 'Throw ArgumentError', - onPressed: () => throwUnhandledException( - ArgumentError('This is an ArgumentError.')), - ), - InstabugButton( - text: 'Throw RangeError', - onPressed: () => throwUnhandledException( - RangeError.range(5, 0, 3, 'Index out of range')), - ), - InstabugButton( - text: 'Throw FormatException', - onPressed: () => - throwUnhandledException(UnsupportedError('Invalid format.')), - ), - InstabugButton( - text: 'Throw NoSuchMethodError', - onPressed: () { - // This intentionally triggers a NoSuchMethodError - dynamic obj; - throwUnhandledException(obj.methodThatDoesNotExist()); - }, - ), - const InstabugButton( - text: 'Throw Native Fatal Crash', - onPressed: InstabugFlutterExampleMethodChannel.sendNativeFatalCrash, - ), - const InstabugButton( - text: 'Send Native Fatal Hang', - onPressed: InstabugFlutterExampleMethodChannel.sendNativeFatalHang, - ), - Platform.isAndroid - ? const InstabugButton( - text: 'Send Native ANR', - onPressed: InstabugFlutterExampleMethodChannel.sendAnr, - ) - : const SizedBox.shrink(), - const InstabugButton( - text: 'Throw Unhandled Native OOM Exception', - onPressed: InstabugFlutterExampleMethodChannel.sendOom, - ), - ], - ); - } -} - -class ApmPage extends StatefulWidget { - static const screenName = 'apm'; - const ApmPage({Key? key}) : super(key: key); - - @override - State createState() => _ApmPageState(); -} - -class _ApmPageState extends State { - void _navigateToScreenLoading() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ScreenLoadingPage(), - settings: const RouteSettings( - name: ScreenLoadingPage.screenName, - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - return Page( - title: 'APM', - children: [ - SectionTitle('Network'), - const NetworkContent(), - SectionTitle('Traces'), - const TracesContent(), - SectionTitle('Flows'), - const FlowsContent(), - SectionTitle('Screen Loading'), - SizedBox.fromSize( - size: const Size.fromHeight(12), - ), - InstabugButton( - text: 'Screen Loading', - onPressed: _navigateToScreenLoading, - ), - SizedBox.fromSize( - size: const Size.fromHeight(12), - ), - ], - ); - } -} - -class NetworkContent extends StatefulWidget { - const NetworkContent({Key? key}) : super(key: key); - final String defaultRequestUrl = - 'https://jsonplaceholder.typicode.com/posts/1'; - - @override - State createState() => _NetworkContentState(); -} - -class _NetworkContentState extends State { - final endpointUrlController = TextEditingController(); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - InstabugClipboardInput( - label: 'Endpoint Url', - controller: endpointUrlController, - ), - InstabugButton( - text: 'Send Request To Url', - onPressed: () => _sendRequestToUrl(endpointUrlController.text), - ), - ], - ); - } - - void _sendRequestToUrl(String text) async { - try { - String url = text.trim().isEmpty ? widget.defaultRequestUrl : text; - final response = await http.get(Uri.parse(url)); - - // Handle the response here - if (response.statusCode == 200) { - final jsonData = json.decode(response.body); - log(jsonEncode(jsonData)); - } else { - log('Request failed with status: ${response.statusCode}'); - } - } catch (e) { - log('Error sending request: $e'); - } - } -} - -class TracesContent extends StatefulWidget { - const TracesContent({Key? key}) : super(key: key); - - @override - State createState() => _TracesContentState(); -} - -class _TracesContentState extends State { - final traceNameController = TextEditingController(); - final traceKeyAttributeController = TextEditingController(); - final traceValueAttributeController = TextEditingController(); - - bool? didTraceEnd; - - Trace? trace; - - @override - Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; - return Column( - children: [ - InstabugTextField( - label: 'Trace name', - labelStyle: textTheme.labelMedium, - controller: traceNameController, - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start Trace', - onPressed: () => _startTrace(traceNameController.text), - margin: const EdgeInsetsDirectional.only( - start: 20.0, - end: 10.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start Trace With Delay', - onPressed: () => _startTrace( - traceNameController.text, - delayInMilliseconds: 5000, - ), - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Trace Key Attribute', - controller: traceKeyAttributeController, - labelStyle: textTheme.labelMedium, - margin: const EdgeInsetsDirectional.only( - end: 10.0, - start: 20.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Trace Value Attribute', - labelStyle: textTheme.labelMedium, - controller: traceValueAttributeController, - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - InstabugButton( - text: 'Set Trace Attribute', - onPressed: () => _setTraceAttribute( - trace, - traceKeyAttribute: traceKeyAttributeController.text, - traceValueAttribute: traceValueAttributeController.text, - ), - ), - InstabugButton( - text: 'End Trace', - onPressed: () => _endTrace(), - ), - ], - ); - } - - void _startTrace( - String traceName, { - int delayInMilliseconds = 0, - }) { - if (traceName.trim().isNotEmpty) { - log('_startTrace — traceName: $traceName, delay in Milliseconds: $delayInMilliseconds'); - log('traceName: $traceName'); - Future.delayed( - Duration(milliseconds: delayInMilliseconds), - () => APM - .startExecutionTrace(traceName) - .then((value) => trace = value)); - } else { - log('startTrace - Please enter a trace name'); - } - } - - void _endTrace() { - if (didTraceEnd == true) { - log('_endTrace — Please, start a new trace before setting attributes.'); - } - if (trace == null) { - log('_endTrace — Please, start a trace before ending it.'); - } - log('_endTrace — ending Trace.'); - trace?.end(); - didTraceEnd = true; - } - - void _setTraceAttribute( - Trace? trace, { - required String traceKeyAttribute, - required String traceValueAttribute, - }) { - if (trace == null) { - log('_setTraceAttribute — Please, start a trace before setting attributes.'); - } - if (didTraceEnd == true) { - log('_setTraceAttribute — Please, start a new trace before setting attributes.'); - } - if (traceKeyAttribute.trim().isEmpty) { - log('_setTraceAttribute — Please, fill the trace key attribute input before settings attributes.'); - } - if (traceValueAttribute.trim().isEmpty) { - log('_setTraceAttribute — Please, fill the trace value attribute input before settings attributes.'); - } - log('_setTraceAttribute — setting attributes -> key: $traceKeyAttribute, value: $traceValueAttribute.'); - trace?.setAttribute(traceKeyAttribute, traceValueAttribute); - } -} - -class ComplexPage extends StatefulWidget { - static const initialDepth = 10; - static const initialBreadth = 2; - static const screenName = 'complex'; - final bool isMonitored; - - const ComplexPage({ - Key? key, - this.isMonitored = false, - }) : super(key: key); - - const ComplexPage.monitored({ - Key? key, - this.isMonitored = true, - }) : super(key: key); - - @override - State createState() => _ComplexPageState(); -} - -class _ComplexPageState extends State { - final depthController = TextEditingController(); - final breadthController = TextEditingController(); - int depth = ComplexPage.initialDepth; - int breadth = ComplexPage.initialBreadth; - GlobalKey _reloadKey = GlobalKey(); - - @override - void initState() { - super.initState(); - depthController.text = depth.toString(); - breadthController.text = breadth.toString(); - } - - void _handleRender() { - setState(() { - breadth = - int.tryParse(breadthController.text) ?? ComplexPage.initialBreadth; - depth = int.tryParse(depthController.text) ?? ComplexPage.initialBreadth; - _reloadKey = GlobalKey(); - }); - } - - void _resetDidStartScreenLoading() { - ScreenLoadingManager.I.resetDidStartScreenLoading(); - } - - void _resetDidReportScreenLoading() { - ScreenLoadingManager.I.resetDidReportScreenLoading(); - } - - void _resetDidExtendScreenLoading() { - ScreenLoadingManager.I.resetDidExtendScreenLoading(); - } - - void _enableScreenLoading() { - APM.setScreenLoadingEnabled(true); - } - - void _disableScreenLoading() { - APM.setScreenLoadingEnabled(false); - } - - @override - Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; - return _buildPage(textTheme); - } - - Widget _buildPage(TextTheme textTheme) { - final content = [ - InstabugTextField( - label: 'Depth (default: ${ComplexPage.initialDepth})', - labelStyle: textTheme.labelMedium, - controller: depthController, - ), - InstabugTextField( - label: 'Breadth (default: ${ComplexPage.initialBreadth})', - labelStyle: textTheme.labelMedium, - controller: breadthController, - ), - InstabugButton( - onPressed: _handleRender, - text: 'Render', - ), - SizedBox.fromSize( - size: const Size.fromHeight( - 12.0, - ), - ), - InstabugButton( - onPressed: _enableScreenLoading, - text: 'Enable Screen loading', - ), - InstabugButton( - onPressed: _disableScreenLoading, - text: 'Disable Screen Loading', - ), - InstabugButton( - onPressed: _resetDidStartScreenLoading, - text: 'Reset Did Start Screen Loading', - ), - InstabugButton( - onPressed: _resetDidReportScreenLoading, - text: 'Reset Did Report Screen Loading', - ), - InstabugButton( - onPressed: _resetDidExtendScreenLoading, - text: 'Reset Did Extend Screen Loading', - ), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: NestedView( - depth: depth, - breadth: breadth, - ), - ), - ]; - - if (widget.isMonitored) { - return KeyedSubtree( - key: _reloadKey, - child: InstabugCaptureScreenLoading( - screenName: ComplexPage.screenName, - child: Page( - title: 'Monitored Complex', - children: content, - ), - ), - ); - } else { - return Page( - title: 'Complex', - children: content, - ); - } - } -} - -class ScreenLoadingPage extends StatefulWidget { - static const screenName = 'screenLoading'; - const ScreenLoadingPage({Key? key}) : super(key: key); - - @override - State createState() => _ScreenLoadingPageState(); -} - -class _ScreenLoadingPageState extends State { - final durationController = TextEditingController(); - GlobalKey _reloadKey = GlobalKey(); - final List _capturedWidgets = []; - void _render() { - setState(() { - // Key can be changed to force reload and re-render - _reloadKey = GlobalKey(); - }); - } - - void _addCapturedWidget() { - setState(() { - debugPrint('adding captured widget'); - _capturedWidgets.add(0); - }); - } - - ///This is the production implementation as [APM.endScreenLoading()] is the method which users use from [APM] class - void _extendScreenLoading() async { - APM.endScreenLoading(); - } - - ///This is a testing implementation as [APM.endScreenLoadingCP()] is marked as @internal method, - ///Therefor we check if SCL is enabled before proceeding - ///This check is internally done inside the production method [APM.endScreenLoading()] - void _extendScreenLoadingTestingEnvironment() async { - final isScreenLoadingEnabled = await APM.isScreenLoadingEnabled(); - if (isScreenLoadingEnabled) { - final currentUiTrace = ScreenLoadingManager.I.currentUiTrace; - final currentScreenLoadingTrace = - ScreenLoadingManager.I.currentScreenLoadingTrace; - final extendedEndTime = - (currentScreenLoadingTrace?.endTimeInMicroseconds ?? 0) + - (int.tryParse(durationController.text.toString()) ?? 0); - APM.endScreenLoadingCP( - extendedEndTime, - currentUiTrace?.traceId ?? 0, - ); - } else { - debugPrint( - 'Screen loading monitoring is disabled, skipping ending screen loading monitoring with APM.endScreenLoading().\n' - 'Please refer to the documentation for how to enable screen loading monitoring in your app: ' - 'https://docs.instabug.com/docs/flutter-apm-screen-loading#disablingenabling-screen-loading-tracking' - "If Screen Loading is enabled but you're still seeing this message, please reach out to support.", - ); - } - } - - void _navigateToComplexPage() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ComplexPage.monitored(), - settings: const RouteSettings( - name: ComplexPage.screenName, - ), - ), - ); - } - - void _navigateToMonitoredScreenCapturePrematureExtensionPage() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const InstabugCaptureScreenLoading( - screenName: ScreenCapturePrematureExtensionPage.screenName, - child: ScreenCapturePrematureExtensionPage(), - ), - settings: const RouteSettings( - name: ScreenCapturePrematureExtensionPage.screenName, - ), - ), - ); - } - - @override - Widget build(BuildContext context) { - return Page( - title: 'Screen Loading', - floatingActionButton: Container( - height: 40, - child: FloatingActionButton( - tooltip: 'Add', - onPressed: _addCapturedWidget, - child: const Icon(Icons.add, color: Colors.white, size: 28), - ), - ), - children: [ - SectionTitle('6x InstabugCaptureScreen'), - KeyedSubtree( - key: _reloadKey, - child: InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: InstabugCaptureScreenLoading( - screenName: 'different screen name', - child: InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: Container( - margin: const EdgeInsets.only(top: 12), - child: InstabugButton( - text: 'Reload', - onPressed: _render, // Call _render function here - ), - ), - ), - ), - ), - ), - ), - ), - ), - InstabugTextField( - label: 'Duration', - controller: durationController, - keyboardType: TextInputType.number, - ), - Container( - margin: const EdgeInsets.only(top: 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - InstabugButton( - text: 'Extend Screen Loading (Testing)', - onPressed: _extendScreenLoadingTestingEnvironment, - ), - InstabugButton( - text: 'Extend Screen Loading (Production)', - onPressed: _extendScreenLoading, - ), - ], - )), - InstabugButton( - text: 'Monitored Complex Page', - onPressed: _navigateToComplexPage, - ), - InstabugButton( - text: 'Screen Capture Premature Extension Page', - onPressed: _navigateToMonitoredScreenCapturePrematureExtensionPage, - ), - SectionTitle('Dynamic Screen Loading list'), - SizedBox( - height: 100, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 5, childAspectRatio: 5), - reverse: false, - shrinkWrap: true, - itemCount: _capturedWidgets.length, - itemBuilder: (context, index) { - return InstabugCaptureScreenLoading( - screenName: ScreenLoadingPage.screenName, - child: Text(index.toString()), - ); - }, - ), - ), - ), - SizedBox.fromSize( - size: const Size.fromHeight(12), - ), - ], + home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } - -class ScreenCapturePrematureExtensionPage extends StatefulWidget { - static const screenName = 'screenCapturePrematureExtension'; - const ScreenCapturePrematureExtensionPage({Key? key}) : super(key: key); - - @override - State createState() => - _ScreenCapturePrematureExtensionPageState(); -} - -class _ScreenCapturePrematureExtensionPageState - extends State { - void _extendScreenLoading() { - APM.endScreenLoading(); - } - - @override - Widget build(BuildContext context) { - _extendScreenLoading(); - return const Page( - title: 'Screen Capture Premature Extension', - children: [ - Text( - 'This page calls endScreenLoading before it fully renders allowing us to test the scenario of premature extension of screen loading'), - ], - ); - } -} - -class NestedView extends StatelessWidget { - final int depth; - final int breadth; - final Widget? child; - - const NestedView({ - Key? key, - this.depth = ComplexPage.initialDepth, - this.breadth = ComplexPage.initialDepth, - this.child, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - if (depth == 0) { - return child ?? const SizedBox.shrink(); - } - - return Container( - decoration: BoxDecoration( - border: Border.all(), - ), - padding: const EdgeInsets.all(1), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('$depth'), - Row( - children: List.generate( - breadth, - (index) => NestedView( - depth: depth - 1, - breadth: breadth, - child: child, - ), - ), - ), - ], - ), - ); - } -} - -class FlowsContent extends StatefulWidget { - const FlowsContent({Key? key}) : super(key: key); - - @override - State createState() => _FlowsContentState(); -} - -class _FlowsContentState extends State { - final flowNameController = TextEditingController(); - final flowKeyAttributeController = TextEditingController(); - final flowValueAttributeController = TextEditingController(); - - bool? didFlowEnd; - - @override - Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; - return Column( - children: [ - InstabugTextField( - label: 'Flow name', - labelStyle: textTheme.labelMedium, - controller: flowNameController, - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start Flow', - onPressed: () => _startFlow(flowNameController.text), - margin: const EdgeInsetsDirectional.only( - start: 20.0, - end: 10.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugButton.smallFontSize( - text: 'Start flow With Delay', - onPressed: () => _startFlow( - flowNameController.text, - delayInMilliseconds: 5000, - ), - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - Row( - children: [ - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Flow Key Attribute', - controller: flowKeyAttributeController, - labelStyle: textTheme.labelMedium, - margin: const EdgeInsetsDirectional.only( - end: 10.0, - start: 20.0, - ), - ), - ), - Flexible( - flex: 5, - child: InstabugTextField( - label: 'Flow Value Attribute', - labelStyle: textTheme.labelMedium, - controller: flowValueAttributeController, - margin: const EdgeInsetsDirectional.only( - start: 10.0, - end: 20.0, - ), - ), - ), - ], - ), - SizedBox.fromSize( - size: const Size.fromHeight(10.0), - ), - InstabugButton( - text: 'Set Flow Attribute', - onPressed: () => _setFlowAttribute( - flowNameController.text, - flowKeyAttribute: flowKeyAttributeController.text, - flowValueAttribute: flowValueAttributeController.text, - ), - ), - InstabugButton( - text: 'End Flow', - onPressed: () => _endFlow(flowNameController.text), - ), - ], - ); - } - - void _startFlow( - String flowName, { - int delayInMilliseconds = 0, - }) { - if (flowName.trim().isNotEmpty) { - log('_startFlow — flowName: $flowName, delay in Milliseconds: $delayInMilliseconds'); - log('flowName: $flowName'); - Future.delayed(Duration(milliseconds: delayInMilliseconds), - () => APM.startFlow(flowName)); - } else { - log('_startFlow - Please enter a flow name'); - } - } - - void _endFlow(String flowName) { - if (flowName.trim().isEmpty) { - log('_endFlow - Please enter a flow name'); - } - if (didFlowEnd == true) { - log('_endFlow — Please, start a new flow before setting attributes.'); - } - log('_endFlow — ending Flow.'); - didFlowEnd = true; - } - - void _setFlowAttribute( - String flowName, { - required String flowKeyAttribute, - required String flowValueAttribute, - }) { - if (flowName.trim().isEmpty) { - log('_endFlow - Please enter a flow name'); - } - if (didFlowEnd == true) { - log('_setFlowAttribute — Please, start a new flow before setting attributes.'); - } - if (flowKeyAttribute.trim().isEmpty) { - log('_setFlowAttribute — Please, fill the flow key attribute input before settings attributes.'); - } - if (flowValueAttribute.trim().isEmpty) { - log('_setFlowAttribute — Please, fill the flow value attribute input before settings attributes.'); - } - log('_setFlowAttribute — setting attributes -> key: $flowKeyAttribute, value: $flowValueAttribute.'); - APM.setFlowAttribute(flowName, flowKeyAttribute, flowValueAttribute); - } -} diff --git a/example/lib/src/app_routes.dart b/example/lib/src/app_routes.dart new file mode 100644 index 000000000..1237d6bf1 --- /dev/null +++ b/example/lib/src/app_routes.dart @@ -0,0 +1,18 @@ +import 'package:flutter/widgets.dart' show BuildContext; +import 'package:instabug_flutter_example/main.dart'; + +final appRoutes = { + /// ["/"] route name should only be used with [onGenerateRoute:] when no + /// Home Widget specified in MaterialApp() other wise the the Flutter engine + /// will throw a Runtime exception deo to Flutter restrictions + + // "/": (BuildContext context) => + // const MyHomePage(title: 'Flutter Demo Home Pag'), + CrashesPage.screenName: (BuildContext context) => const CrashesPage(), + ComplexPage.screenName: (BuildContext context) => const ComplexPage(), + ApmPage.screenName: (BuildContext context) => const ApmPage(), + ScreenLoadingPage.screenName: (BuildContext context) => + const ScreenLoadingPage(), + ScreenCapturePrematureExtensionPage.screenName: (BuildContext context) => + const ScreenCapturePrematureExtensionPage(), +}; diff --git a/example/lib/src/components/fatal_crashes_content.dart b/example/lib/src/components/fatal_crashes_content.dart new file mode 100644 index 000000000..c37f7e01c --- /dev/null +++ b/example/lib/src/components/fatal_crashes_content.dart @@ -0,0 +1,75 @@ +part of '../../main.dart'; + +class FatalCrashesContent extends StatelessWidget { + const FatalCrashesContent({Key? key}) : super(key: key); + + void throwUnhandledException(dynamic error) { + if (error is! Error) { + const String appName = 'Flutter Test App'; + final errorMessage = error?.toString() ?? 'Unknown Error'; + error = Exception('Unhandled Error: $errorMessage from $appName'); + } + throw error; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InstabugButton( + text: 'Throw Exception', + onPressed: () => throwUnhandledException( + Exception('This is a generic exception.')), + ), + InstabugButton( + text: 'Throw StateError', + onPressed: () => + throwUnhandledException(StateError('This is a StateError.')), + ), + InstabugButton( + text: 'Throw ArgumentError', + onPressed: () => throwUnhandledException( + ArgumentError('This is an ArgumentError.')), + ), + InstabugButton( + text: 'Throw RangeError', + onPressed: () => throwUnhandledException( + RangeError.range(5, 0, 3, 'Index out of range')), + ), + InstabugButton( + text: 'Throw FormatException', + onPressed: () => + throwUnhandledException(UnsupportedError('Invalid format.')), + ), + InstabugButton( + text: 'Throw NoSuchMethodError', + onPressed: () { + // This intentionally triggers a NoSuchMethodError + dynamic obj; + throwUnhandledException(obj.methodThatDoesNotExist()); + }, + ), + const InstabugButton( + text: 'Throw Native Fatal Crash', + onPressed: InstabugFlutterExampleMethodChannel.sendNativeFatalCrash, + ), + const InstabugButton( + text: 'Send Native Fatal Hang', + onPressed: InstabugFlutterExampleMethodChannel.sendNativeFatalHang, + ), + Platform.isAndroid + ? const InstabugButton( + text: 'Send Native ANR', + onPressed: InstabugFlutterExampleMethodChannel.sendAnr, + ) + : const SizedBox.shrink(), + const InstabugButton( + text: 'Throw Unhandled Native OOM Exception', + onPressed: InstabugFlutterExampleMethodChannel.sendOom, + ), + ], + ); + } +} \ No newline at end of file diff --git a/example/lib/src/components/flows_content.dart b/example/lib/src/components/flows_content.dart new file mode 100644 index 000000000..ecd8163f9 --- /dev/null +++ b/example/lib/src/components/flows_content.dart @@ -0,0 +1,151 @@ +part of '../../main.dart'; + +class FlowsContent extends StatefulWidget { + const FlowsContent({Key? key}) : super(key: key); + + @override + State createState() => _FlowsContentState(); +} + +class _FlowsContentState extends State { + final flowNameController = TextEditingController(); + final flowKeyAttributeController = TextEditingController(); + final flowValueAttributeController = TextEditingController(); + + bool? didFlowEnd; + + @override + Widget build(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + return Column( + children: [ + InstabugTextField( + label: 'Flow name', + labelStyle: textTheme.labelMedium, + controller: flowNameController, + ), + SizedBox.fromSize( + size: const Size.fromHeight(10.0), + ), + Row( + children: [ + Flexible( + flex: 5, + child: InstabugButton.smallFontSize( + text: 'Start Flow', + onPressed: () => _startFlow(flowNameController.text), + margin: const EdgeInsetsDirectional.only( + start: 20.0, + end: 10.0, + ), + ), + ), + Flexible( + flex: 5, + child: InstabugButton.smallFontSize( + text: 'Start flow With Delay', + onPressed: () => _startFlow( + flowNameController.text, + delayInMilliseconds: 5000, + ), + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 20.0, + ), + ), + ), + ], + ), + Row( + children: [ + Flexible( + flex: 5, + child: InstabugTextField( + label: 'Flow Key Attribute', + controller: flowKeyAttributeController, + labelStyle: textTheme.labelMedium, + margin: const EdgeInsetsDirectional.only( + end: 10.0, + start: 20.0, + ), + ), + ), + Flexible( + flex: 5, + child: InstabugTextField( + label: 'Flow Value Attribute', + labelStyle: textTheme.labelMedium, + controller: flowValueAttributeController, + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 20.0, + ), + ), + ), + ], + ), + SizedBox.fromSize( + size: const Size.fromHeight(10.0), + ), + InstabugButton( + text: 'Set Flow Attribute', + onPressed: () => _setFlowAttribute( + flowNameController.text, + flowKeyAttribute: flowKeyAttributeController.text, + flowValueAttribute: flowValueAttributeController.text, + ), + ), + InstabugButton( + text: 'End Flow', + onPressed: () => _endFlow(flowNameController.text), + ), + ], + ); + } + + void _startFlow( + String flowName, { + int delayInMilliseconds = 0, + }) { + if (flowName.trim().isNotEmpty) { + log('_startFlow — flowName: $flowName, delay in Milliseconds: $delayInMilliseconds'); + log('flowName: $flowName'); + Future.delayed(Duration(milliseconds: delayInMilliseconds), + () => APM.startFlow(flowName)); + } else { + log('_startFlow - Please enter a flow name'); + } + } + + void _endFlow(String flowName) { + if (flowName.trim().isEmpty) { + log('_endFlow - Please enter a flow name'); + } + if (didFlowEnd == true) { + log('_endFlow — Please, start a new flow before setting attributes.'); + } + log('_endFlow — ending Flow.'); + didFlowEnd = true; + } + + void _setFlowAttribute( + String flowName, { + required String flowKeyAttribute, + required String flowValueAttribute, + }) { + if (flowName.trim().isEmpty) { + log('_endFlow - Please enter a flow name'); + } + if (didFlowEnd == true) { + log('_setFlowAttribute — Please, start a new flow before setting attributes.'); + } + if (flowKeyAttribute.trim().isEmpty) { + log('_setFlowAttribute — Please, fill the flow key attribute input before settings attributes.'); + } + if (flowValueAttribute.trim().isEmpty) { + log('_setFlowAttribute — Please, fill the flow value attribute input before settings attributes.'); + } + log('_setFlowAttribute — setting attributes -> key: $flowKeyAttribute, value: $flowValueAttribute.'); + APM.setFlowAttribute(flowName, flowKeyAttribute, flowValueAttribute); + } +} diff --git a/example/lib/src/components/network_content.dart b/example/lib/src/components/network_content.dart new file mode 100644 index 000000000..c364ff89d --- /dev/null +++ b/example/lib/src/components/network_content.dart @@ -0,0 +1,47 @@ +part of '../../main.dart'; + +class NetworkContent extends StatefulWidget { + const NetworkContent({Key? key}) : super(key: key); + final String defaultRequestUrl = + 'https://jsonplaceholder.typicode.com/posts/1'; + + @override + State createState() => _NetworkContentState(); +} + +class _NetworkContentState extends State { + final endpointUrlController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + InstabugClipboardInput( + label: 'Endpoint Url', + controller: endpointUrlController, + ), + InstabugButton( + text: 'Send Request To Url', + onPressed: () => _sendRequestToUrl(endpointUrlController.text), + ), + ], + ); + } + + void _sendRequestToUrl(String text) async { + try { + String url = text.trim().isEmpty ? widget.defaultRequestUrl : text; + final response = await http.get(Uri.parse(url)); + + // Handle the response here + if (response.statusCode == 200) { + final jsonData = json.decode(response.body); + log(jsonEncode(jsonData)); + } else { + log('Request failed with status: ${response.statusCode}'); + } + } catch (e) { + log('Error sending request: $e'); + } + } +} diff --git a/example/lib/src/components/non_fatal_crashes_content.dart b/example/lib/src/components/non_fatal_crashes_content.dart new file mode 100644 index 000000000..38aa9160b --- /dev/null +++ b/example/lib/src/components/non_fatal_crashes_content.dart @@ -0,0 +1,66 @@ +part of '../../main.dart'; + +class NonFatalCrashesContent extends StatelessWidget { + const NonFatalCrashesContent({Key? key}) : super(key: key); + + void throwHandledException(dynamic error) { + try { + if (error is! Error) { + const String appName = 'Flutter Test App'; + final errorMessage = error?.toString() ?? 'Unknown Error'; + error = Exception('Handled Error: $errorMessage from $appName'); + } + throw error; + } catch (err) { + if (err is Error) { + log('throwHandledException: Crash report for ${err.runtimeType} is Sent!', + name: 'NonFatalCrashesWidget'); + } + } + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + InstabugButton( + text: 'Throw Exception', + onPressed: () => + throwHandledException(Exception('This is a generic exception.')), + ), + InstabugButton( + text: 'Throw StateError', + onPressed: () => + throwHandledException(StateError('This is a StateError.')), + ), + InstabugButton( + text: 'Throw ArgumentError', + onPressed: () => + throwHandledException(ArgumentError('This is an ArgumentError.')), + ), + InstabugButton( + text: 'Throw RangeError', + onPressed: () => throwHandledException( + RangeError.range(5, 0, 3, 'Index out of range')), + ), + InstabugButton( + text: 'Throw FormatException', + onPressed: () => + throwHandledException(UnsupportedError('Invalid format.')), + ), + InstabugButton( + text: 'Throw NoSuchMethodError', + onPressed: () { + dynamic obj; + throwHandledException(obj.methodThatDoesNotExist()); + }, + ), + const InstabugButton( + text: 'Throw Handled Native Exception', + onPressed: + InstabugFlutterExampleMethodChannel.sendNativeNonFatalCrash, + ), + ], + ); + } +} \ No newline at end of file diff --git a/example/lib/src/components/page.dart b/example/lib/src/components/page.dart new file mode 100644 index 000000000..de61d4b65 --- /dev/null +++ b/example/lib/src/components/page.dart @@ -0,0 +1,35 @@ +part of '../../main.dart'; + +class Page extends StatelessWidget { + final String title; + final GlobalKey? scaffoldKey; + final List children; + final Widget? floatingActionButton; + final FloatingActionButtonLocation? floatingActionButtonLocation; + + const Page({ + Key? key, + required this.title, + this.scaffoldKey, + this.floatingActionButton, + this.floatingActionButtonLocation, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + key: scaffoldKey, + appBar: AppBar(title: Text(title)), + body: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + padding: const EdgeInsets.only(top: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: children, + )), + floatingActionButton: floatingActionButton, + floatingActionButtonLocation: floatingActionButtonLocation, + ); + } +} diff --git a/example/lib/src/components/traces_content.dart b/example/lib/src/components/traces_content.dart new file mode 100644 index 000000000..888460d43 --- /dev/null +++ b/example/lib/src/components/traces_content.dart @@ -0,0 +1,157 @@ +part of '../../main.dart'; + +class TracesContent extends StatefulWidget { + const TracesContent({Key? key}) : super(key: key); + + @override + State createState() => _TracesContentState(); +} + +class _TracesContentState extends State { + final traceNameController = TextEditingController(); + final traceKeyAttributeController = TextEditingController(); + final traceValueAttributeController = TextEditingController(); + + bool? didTraceEnd; + + Trace? trace; + + @override + Widget build(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + return Column( + children: [ + InstabugTextField( + label: 'Trace name', + labelStyle: textTheme.labelMedium, + controller: traceNameController, + ), + SizedBox.fromSize( + size: const Size.fromHeight(10.0), + ), + Row( + children: [ + Flexible( + flex: 5, + child: InstabugButton.smallFontSize( + text: 'Start Trace', + onPressed: () => _startTrace(traceNameController.text), + margin: const EdgeInsetsDirectional.only( + start: 20.0, + end: 10.0, + ), + ), + ), + Flexible( + flex: 5, + child: InstabugButton.smallFontSize( + text: 'Start Trace With Delay', + onPressed: () => _startTrace( + traceNameController.text, + delayInMilliseconds: 5000, + ), + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 20.0, + ), + ), + ), + ], + ), + Row( + children: [ + Flexible( + flex: 5, + child: InstabugTextField( + label: 'Trace Key Attribute', + controller: traceKeyAttributeController, + labelStyle: textTheme.labelMedium, + margin: const EdgeInsetsDirectional.only( + end: 10.0, + start: 20.0, + ), + ), + ), + Flexible( + flex: 5, + child: InstabugTextField( + label: 'Trace Value Attribute', + labelStyle: textTheme.labelMedium, + controller: traceValueAttributeController, + margin: const EdgeInsetsDirectional.only( + start: 10.0, + end: 20.0, + ), + ), + ), + ], + ), + SizedBox.fromSize( + size: const Size.fromHeight(10.0), + ), + InstabugButton( + text: 'Set Trace Attribute', + onPressed: () => _setTraceAttribute( + trace, + traceKeyAttribute: traceKeyAttributeController.text, + traceValueAttribute: traceValueAttributeController.text, + ), + ), + InstabugButton( + text: 'End Trace', + onPressed: () => _endTrace(), + ), + ], + ); + } + + void _startTrace( + String traceName, { + int delayInMilliseconds = 0, + }) { + if (traceName.trim().isNotEmpty) { + log('_startTrace — traceName: $traceName, delay in Milliseconds: $delayInMilliseconds'); + log('traceName: $traceName'); + Future.delayed( + Duration(milliseconds: delayInMilliseconds), + () => APM + .startExecutionTrace(traceName) + .then((value) => trace = value)); + } else { + log('startTrace - Please enter a trace name'); + } + } + + void _endTrace() { + if (didTraceEnd == true) { + log('_endTrace — Please, start a new trace before setting attributes.'); + } + if (trace == null) { + log('_endTrace — Please, start a trace before ending it.'); + } + log('_endTrace — ending Trace.'); + trace?.end(); + didTraceEnd = true; + } + + void _setTraceAttribute( + Trace? trace, { + required String traceKeyAttribute, + required String traceValueAttribute, + }) { + if (trace == null) { + log('_setTraceAttribute — Please, start a trace before setting attributes.'); + } + if (didTraceEnd == true) { + log('_setTraceAttribute — Please, start a new trace before setting attributes.'); + } + if (traceKeyAttribute.trim().isEmpty) { + log('_setTraceAttribute — Please, fill the trace key attribute input before settings attributes.'); + } + if (traceValueAttribute.trim().isEmpty) { + log('_setTraceAttribute — Please, fill the trace value attribute input before settings attributes.'); + } + log('_setTraceAttribute — setting attributes -> key: $traceKeyAttribute, value: $traceValueAttribute.'); + trace?.setAttribute(traceKeyAttribute, traceValueAttribute); + } +} diff --git a/example/lib/src/screens/apm_page.dart b/example/lib/src/screens/apm_page.dart new file mode 100644 index 000000000..8580e203f --- /dev/null +++ b/example/lib/src/screens/apm_page.dart @@ -0,0 +1,50 @@ +part of '../../main.dart'; + +class ApmPage extends StatefulWidget { + static const screenName = 'apm'; + + const ApmPage({Key? key}) : super(key: key); + + @override + State createState() => _ApmPageState(); +} + +class _ApmPageState extends State { + void _navigateToScreenLoading() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ScreenLoadingPage(), + settings: const RouteSettings( + name: ScreenLoadingPage.screenName, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Page( + title: 'APM', + children: [ + const SectionTitle('Network'), + const NetworkContent(), + const SectionTitle('Traces'), + const TracesContent(), + const SectionTitle('Flows'), + const FlowsContent(), + const SectionTitle('Screen Loading'), + SizedBox.fromSize( + size: const Size.fromHeight(12), + ), + InstabugButton( + text: 'Screen Loading', + onPressed: _navigateToScreenLoading, + ), + SizedBox.fromSize( + size: const Size.fromHeight(12), + ), + ], + ); + } +} diff --git a/example/lib/src/screens/complex_page.dart b/example/lib/src/screens/complex_page.dart new file mode 100644 index 000000000..6e1b89498 --- /dev/null +++ b/example/lib/src/screens/complex_page.dart @@ -0,0 +1,141 @@ +part of '../../main.dart'; + +class ComplexPage extends StatefulWidget { + static const initialDepth = 10; + static const initialBreadth = 2; + static const screenName = 'complex'; + final bool isMonitored; + + const ComplexPage({ + Key? key, + this.isMonitored = false, + }) : super(key: key); + + const ComplexPage.monitored({ + Key? key, + this.isMonitored = true, + }) : super(key: key); + + @override + State createState() => _ComplexPageState(); +} + +class _ComplexPageState extends State { + final depthController = TextEditingController(); + final breadthController = TextEditingController(); + int depth = ComplexPage.initialDepth; + int breadth = ComplexPage.initialBreadth; + GlobalKey _reloadKey = GlobalKey(); + + @override + void initState() { + super.initState(); + depthController.text = depth.toString(); + breadthController.text = breadth.toString(); + } + + void _handleRender() { + setState(() { + breadth = + int.tryParse(breadthController.text) ?? ComplexPage.initialBreadth; + depth = int.tryParse(depthController.text) ?? ComplexPage.initialBreadth; + _reloadKey = GlobalKey(); + }); + } + + + void _resetDidStartScreenLoading() { + ScreenLoadingManager.I.resetDidStartScreenLoading(); + } + + void _resetDidReportScreenLoading() { + ScreenLoadingManager.I.resetDidReportScreenLoading(); + } + + void _resetDidExtendScreenLoading() { + ScreenLoadingManager.I.resetDidExtendScreenLoading(); + } + + void _enableScreenLoading() { + APM.setScreenLoadingEnabled(true); + } + + void _disableScreenLoading() { + APM.setScreenLoadingEnabled(false); + } + + @override + Widget build(BuildContext context) { + final textTheme = Theme.of(context).textTheme; + return _buildPage(textTheme); + } + + Widget _buildPage(TextTheme textTheme) { + final content = [ + InstabugTextField( + label: 'Depth (default: ${ComplexPage.initialDepth})', + labelStyle: textTheme.labelMedium, + controller: depthController, + ), + InstabugTextField( + label: 'Breadth (default: ${ComplexPage.initialBreadth})', + labelStyle: textTheme.labelMedium, + controller: breadthController, + ), + InstabugButton( + onPressed: _handleRender, + text: 'Render', + ), + SizedBox.fromSize( + size: const Size.fromHeight( + 12.0, + ), + ), + InstabugButton( + onPressed: _enableScreenLoading, + text: 'Enable Screen loading', + ), + InstabugButton( + onPressed: _disableScreenLoading, + text: 'Disable Screen Loading', + ), + InstabugButton( + onPressed: _resetDidStartScreenLoading, + text: 'Reset Did Start Screen Loading', + ), + InstabugButton( + onPressed: _resetDidReportScreenLoading, + text: 'Reset Did Report Screen Loading', + ), + InstabugButton( + onPressed: _resetDidExtendScreenLoading, + text: 'Reset Did Extend Screen Loading', + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: NestedView( + depth: depth, + breadth: breadth, + ), + ), + ]; + + if (widget.isMonitored) { + return KeyedSubtree( + key: _reloadKey, + child: InstabugCaptureScreenLoading( + screenName: ComplexPage.screenName, + child: Page( + title: 'Monitored Complex', + children: content, + ), + ), + ); + } else { + return Page( + title: 'Complex', + children: content, + ); + } + } +} \ No newline at end of file diff --git a/example/lib/src/screens/crashes_page.dart b/example/lib/src/screens/crashes_page.dart new file mode 100644 index 000000000..421842f57 --- /dev/null +++ b/example/lib/src/screens/crashes_page.dart @@ -0,0 +1,22 @@ +part of '../../main.dart'; + +class CrashesPage extends StatelessWidget { + static const screenName = 'crashes'; + + const CrashesPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const Page( + title: 'Crashes', + children: [ + SectionTitle('Non-Fatal Crashes'), + NonFatalCrashesContent(), + SectionTitle('Fatal Crashes'), + Text('Fatal Crashes can only be tested in release mode'), + Text('Most of these buttons will crash the application'), + FatalCrashesContent(), + ], + ); + } +} \ No newline at end of file diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart new file mode 100644 index 000000000..3163d2642 --- /dev/null +++ b/example/lib/src/screens/my_home_page.dart @@ -0,0 +1,332 @@ +part of '../../main.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final buttonStyle = ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.lightBlue), + foregroundColor: MaterialStateProperty.all(Colors.white), + ); + + List reportTypes = []; + + final primaryColorController = TextEditingController(); + final screenNameController = TextEditingController(); + + void restartInstabug() { + Instabug.setEnabled(false); + Instabug.setEnabled(true); + BugReporting.setInvocationEvents([InvocationEvent.floatingButton]); + } + + void setOnDismissCallback() { + BugReporting.setOnDismissCallback((dismissType, reportType) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('On Dismiss'), + content: Text( + 'onDismiss callback called with $dismissType and $reportType', + ), + ); + }, + ); + }); + } + + void show() { + Instabug.show(); + } + + void reportScreenChange() { + Instabug.reportScreenChange(screenNameController.text); + } + + void sendBugReport() { + BugReporting.show(ReportType.bug, [InvocationOption.emailFieldOptional]); + } + + void sendFeedback() { + BugReporting.show( + ReportType.feedback, [InvocationOption.emailFieldOptional]); + } + + void showNpsSurvey() { + Surveys.showSurvey('pcV_mE2ttqHxT1iqvBxL0w'); + } + + void showManualSurvey() { + Surveys.showSurvey('PMqUZXqarkOR2yGKiENB4w'); + } + + final _scaffoldKey = GlobalKey(); + + void getCurrentSessionReplaylink() async { + final result = await SessionReplay.getSessionReplayLink(); + if (result == null) { + const snackBar = SnackBar( + content: Text('No Link Found'), + ); + ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(snackBar); + } else { + var snackBar = SnackBar( + content: Text(result), + ); + ScaffoldMessenger.of(_scaffoldKey.currentContext!).showSnackBar(snackBar); + } + } + + void showFeatureRequests() { + FeatureRequests.show(); + } + + void toggleReportType(ReportType reportType) { + if (reportTypes.contains(reportType)) { + reportTypes.remove(reportType); + } else { + reportTypes.add(reportType); + } + BugReporting.setReportTypes(reportTypes); + } + + void changeFloatingButtonEdge() { + BugReporting.setFloatingButtonEdge(FloatingButtonEdge.left, 200); + } + + void setInvocationEvent(InvocationEvent invocationEvent) { + BugReporting.setInvocationEvents([invocationEvent]); + } + + void changePrimaryColor() { + String text = 'FF' + primaryColorController.text.replaceAll('#', ''); + Color color = Color(int.parse(text, radix: 16)); + Instabug.setPrimaryColor(color); + } + + void setColorTheme(ColorTheme colorTheme) { + Instabug.setColorTheme(colorTheme); + } + + void _navigateToCrashes() { + ///This way of navigation utilize screenLoading automatic approach [Navigator 1] + Navigator.pushNamed(context, CrashesPage.screenName); + + ///This way of navigation utilize screenLoading manual approach [Navigator 1] + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => const CrashesPage(), + // settings: const RouteSettings(name: CrashesPage.screenName), + // ), + // ); + } + + + void _navigateToApm() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const InstabugCaptureScreenLoading( + screenName: ApmPage.screenName, + child: ApmPage(), + ), + settings: const RouteSettings(name: ApmPage.screenName), + ), + ); + } + + void _navigateToComplex() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ComplexPage(), + settings: const RouteSettings(name: ComplexPage.screenName), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Page( + scaffoldKey: _scaffoldKey, + title: widget.title, + children: [ + Container( + margin: const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0), + child: const Text( + 'Hello Instabug\'s awesome user! The purpose of this application is to show you the different options for customizing the SDK and how easy it is to integrate it to your existing app', + textAlign: TextAlign.center, + ), + ), + InstabugButton( + onPressed: restartInstabug, + text: 'Restart Instabug', + ), + const SectionTitle('Primary Color'), + InstabugTextField( + controller: primaryColorController, + label: 'Enter primary color', + ), + InstabugButton( + text: 'Change Primary Color', + onPressed: changePrimaryColor, + ), + const SectionTitle('Change Invocation Event'), + ButtonBar( + mainAxisSize: MainAxisSize.min, + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton( + onPressed: () => setInvocationEvent(InvocationEvent.none), + style: buttonStyle, + child: const Text('None'), + ), + ElevatedButton( + onPressed: () => setInvocationEvent(InvocationEvent.shake), + style: buttonStyle, + child: const Text('Shake'), + ), + ElevatedButton( + onPressed: () => setInvocationEvent(InvocationEvent.screenshot), + style: buttonStyle, + child: const Text('Screenshot'), + ), + ], + ), + ButtonBar( + mainAxisSize: MainAxisSize.min, + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton( + onPressed: () => + setInvocationEvent(InvocationEvent.floatingButton), + style: buttonStyle, + child: const Text('Floating Button'), + ), + ElevatedButton( + onPressed: () => + setInvocationEvent(InvocationEvent.twoFingersSwipeLeft), + style: buttonStyle, + child: const Text('Two Fingers Swipe Left'), + ), + ], + ), + InstabugButton( + onPressed: show, + text: 'Invoke', + ), + InstabugButton( + onPressed: setOnDismissCallback, + text: 'Set On Dismiss Callback', + ), + const SectionTitle('Repro Steps'), + InstabugTextField( + controller: screenNameController, + label: 'Enter screen name', + ), + InstabugButton( + text: 'Report Screen Change', + onPressed: reportScreenChange, + ), + InstabugButton( + onPressed: sendBugReport, + text: 'Send Bug Report', + ), + InstabugButton( + onPressed: showManualSurvey, + text: 'Show Manual Survey', + ), + const SectionTitle('Change Report Types'), + ButtonBar( + mainAxisSize: MainAxisSize.min, + alignment: MainAxisAlignment.start, + children: [ + ElevatedButton( + onPressed: () => toggleReportType(ReportType.bug), + style: buttonStyle, + child: const Text('Bug'), + ), + ElevatedButton( + onPressed: () => toggleReportType(ReportType.feedback), + style: buttonStyle, + child: const Text('Feedback'), + ), + ElevatedButton( + onPressed: () => toggleReportType(ReportType.question), + style: buttonStyle, + child: const Text('Question'), + ), + ], + ), + InstabugButton( + onPressed: changeFloatingButtonEdge, + text: 'Move Floating Button to Left', + ), + InstabugButton( + onPressed: sendFeedback, + text: 'Send Feedback', + ), + InstabugButton( + onPressed: showNpsSurvey, + text: 'Show NPS Survey', + ), + InstabugButton( + onPressed: showManualSurvey, + text: 'Show Multiple Questions Survey', + ), + InstabugButton( + onPressed: showFeatureRequests, + text: 'Show Feature Requests', + ), + InstabugButton( + onPressed: _navigateToCrashes, + text: 'Crashes', + ), + InstabugButton( + onPressed: _navigateToApm, + text: 'APM', + ), + InstabugButton( + onPressed: _navigateToComplex, + text: 'Complex', + ), + const SectionTitle('Sessions Replay'), + InstabugButton( + onPressed: getCurrentSessionReplaylink, + text: 'Get current session replay link', + ), + const SectionTitle('Color Theme'), + ButtonBar( + mainAxisSize: MainAxisSize.max, + alignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () => setColorTheme(ColorTheme.light), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.white), + foregroundColor: MaterialStateProperty.all(Colors.lightBlue), + ), + child: const Text('Light'), + ), + ElevatedButton( + onPressed: () => setColorTheme(ColorTheme.dark), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all(Colors.black), + foregroundColor: MaterialStateProperty.all(Colors.white), + ), + child: const Text('Dark'), + ), + ], + ), + ], + ); + } +} diff --git a/example/lib/src/screens/screen_capture_premature_extension_page.dart b/example/lib/src/screens/screen_capture_premature_extension_page.dart new file mode 100644 index 000000000..db7bb8556 --- /dev/null +++ b/example/lib/src/screens/screen_capture_premature_extension_page.dart @@ -0,0 +1,30 @@ +part of '../../main.dart'; + +class ScreenCapturePrematureExtensionPage extends StatefulWidget { + static const screenName = 'screenCapturePrematureExtension'; + + const ScreenCapturePrematureExtensionPage({Key? key}) : super(key: key); + + @override + State createState() => + _ScreenCapturePrematureExtensionPageState(); +} + +class _ScreenCapturePrematureExtensionPageState + extends State { + void _extendScreenLoading() { + APM.endScreenLoading(); + } + + @override + Widget build(BuildContext context) { + _extendScreenLoading(); + return const Page( + title: 'Screen Capture Premature Extension', + children: [ + Text( + 'This page calls endScreenLoading before it fully renders allowing us to test the scenario of premature extension of screen loading'), + ], + ); + } +} \ No newline at end of file diff --git a/example/lib/src/screens/screen_loading_page.dart b/example/lib/src/screens/screen_loading_page.dart new file mode 100644 index 000000000..4d687363a --- /dev/null +++ b/example/lib/src/screens/screen_loading_page.dart @@ -0,0 +1,186 @@ +part of '../../main.dart'; + +class ScreenLoadingPage extends StatefulWidget { + static const screenName = 'screenLoading'; + + const ScreenLoadingPage({Key? key}) : super(key: key); + + @override + State createState() => _ScreenLoadingPageState(); +} + +class _ScreenLoadingPageState extends State { + final durationController = TextEditingController(); + GlobalKey _reloadKey = GlobalKey(); + final List _capturedWidgets = []; + + void _render() { + setState(() { + // Key can be changed to force reload and re-render + _reloadKey = GlobalKey(); + }); + } + + void _addCapturedWidget() { + setState(() { + debugPrint('adding captured widget'); + _capturedWidgets.add(0); + }); + } + + ///This is the production implementation as [APM.endScreenLoading()] is the method which users use from [APM] class + void _extendScreenLoading() async { + APM.endScreenLoading(); + } + + ///This is a testing implementation as [APM.endScreenLoadingCP()] is marked as @internal method, + ///Therefor we check if SCL is enabled before proceeding + ///This check is internally done inside the production method [APM.endScreenLoading()] + void _extendScreenLoadingTestingEnvironment() async { + final isScreenLoadingEnabled = await APM.isScreenLoadingEnabled(); + if (isScreenLoadingEnabled) { + final currentUiTrace = ScreenLoadingManager.I.currentUiTrace; + final currentScreenLoadingTrace = + ScreenLoadingManager.I.currentScreenLoadingTrace; + final extendedEndTime = + (currentScreenLoadingTrace?.endTimeInMicroseconds ?? 0) + + (int.tryParse(durationController.text.toString()) ?? 0); + APM.endScreenLoadingCP( + extendedEndTime, + currentUiTrace?.traceId ?? 0, + ); + } else { + debugPrint( + 'Screen loading monitoring is disabled, skipping ending screen loading monitoring with APM.endScreenLoading().\n' + 'Please refer to the documentation for how to enable screen loading monitoring in your app: ' + 'https://docs.instabug.com/docs/flutter-apm-screen-loading#disablingenabling-screen-loading-tracking' + "If Screen Loading is enabled but you're still seeing this message, please reach out to support.", + ); + } + } + + void _navigateToComplexPage() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ComplexPage.monitored(), + settings: const RouteSettings( + name: ComplexPage.screenName, + ), + ), + ); + } + + void _navigateToMonitoredScreenCapturePrematureExtensionPage() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const InstabugCaptureScreenLoading( + screenName: ScreenCapturePrematureExtensionPage.screenName, + child: ScreenCapturePrematureExtensionPage(), + ), + settings: const RouteSettings( + name: ScreenCapturePrematureExtensionPage.screenName, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Page( + title: 'Screen Loading', + floatingActionButton: Container( + height: 40, + child: FloatingActionButton( + tooltip: 'Add', + onPressed: _addCapturedWidget, + child: const Icon(Icons.add, color: Colors.white, size: 28), + ), + ), + children: [ + SectionTitle('6x InstabugCaptureScreen'), + KeyedSubtree( + key: _reloadKey, + child: InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: InstabugCaptureScreenLoading( + screenName: 'different screen name', + child: InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: Container( + margin: const EdgeInsets.only(top: 12), + child: InstabugButton( + text: 'Reload', + onPressed: _render, // Call _render function here + ), + ), + ), + ), + ), + ), + ), + ), + ), + InstabugTextField( + label: 'Duration', + controller: durationController, + keyboardType: TextInputType.number, + ), + Container( + margin: const EdgeInsets.only(top: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + InstabugButton( + text: 'Extend Screen Loading (Testing)', + onPressed: _extendScreenLoadingTestingEnvironment, + ), + InstabugButton( + text: 'Extend Screen Loading (Production)', + onPressed: _extendScreenLoading, + ), + ], + )), + InstabugButton( + text: 'Monitored Complex Page', + onPressed: _navigateToComplexPage, + ), + InstabugButton( + text: 'Screen Capture Premature Extension Page', + onPressed: _navigateToMonitoredScreenCapturePrematureExtensionPage, + ), + SectionTitle('Dynamic Screen Loading list'), + SizedBox( + height: 100, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 5, childAspectRatio: 5), + reverse: false, + shrinkWrap: true, + itemCount: _capturedWidgets.length, + itemBuilder: (context, index) { + return InstabugCaptureScreenLoading( + screenName: ScreenLoadingPage.screenName, + child: Text(index.toString()), + ); + }, + ), + ), + ), + SizedBox.fromSize( + size: const Size.fromHeight(12), + ), + ], + ); + } +} \ No newline at end of file diff --git a/example/lib/src/widget/nested_view.dart b/example/lib/src/widget/nested_view.dart new file mode 100644 index 000000000..83401539d --- /dev/null +++ b/example/lib/src/widget/nested_view.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_flutter_example/main.dart'; + +class NestedView extends StatelessWidget { + final int depth; + final int breadth; + final Widget? child; + + const NestedView({ + Key? key, + this.depth = ComplexPage.initialDepth, + this.breadth = ComplexPage.initialDepth, + this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (depth == 0) { + return child ?? const SizedBox.shrink(); + } + + return Container( + decoration: BoxDecoration( + border: Border.all(), + ), + padding: const EdgeInsets.all(1), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('$depth'), + Row( + children: List.generate( + breadth, + (index) => NestedView( + depth: depth - 1, + breadth: breadth, + child: child, + ), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/example/lib/src/widget/section_title.dart b/example/lib/src/widget/section_title.dart new file mode 100644 index 000000000..2c0509fa8 --- /dev/null +++ b/example/lib/src/widget/section_title.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class SectionTitle extends StatelessWidget { + final String text; + + const SectionTitle(this.text, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 20.0, left: 20.0), + child: Text( + text, + textAlign: TextAlign.left, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ); + } +} diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index f5c73a707..d2beb1092 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -2,7 +2,7 @@ import 'dart:async'; -import 'package:flutter/widgets.dart' show WidgetBuilder; +import 'package:flutter/widgets.dart' show WidgetBuilder, debugPrint; import 'package:instabug_flutter/src/generated/apm.api.g.dart'; import 'package:instabug_flutter/src/models/network_data.dart'; import 'package:instabug_flutter/src/models/trace.dart'; @@ -222,7 +222,10 @@ class APM { /// Wraps the given routes with [InstabugCaptureScreenLoading] widgets. /// This allows Instabug to automatically capture screen loading times. static Map wrapRoutes( - Map routes) { - return ScreenLoadingManager.wrapRoutes(routes); + Map routes, { + List exclude = const [], + }) { + final x = ScreenLoadingManager.wrapRoutes(routes, exclude: exclude); + return x; } } diff --git a/lib/src/utils/screen_loading/route_wrapper.dart b/lib/src/utils/screen_loading/route_wrapper.dart index bf75f455b..8ccd2db3e 100644 --- a/lib/src/utils/screen_loading/route_wrapper.dart +++ b/lib/src/utils/screen_loading/route_wrapper.dart @@ -14,27 +14,38 @@ class RouteWrapper extends StatelessWidget { /// The initial route to navigate to. final String? initialRoute; + final List exclude; + /// Creates a new instance of [RouteWrapper]. const RouteWrapper( - {Key? key, required this.child, required this.routes, this.initialRoute}) + {Key? key, + required this.child, + required this.routes, + this.initialRoute, + this.exclude = const []}) : super(key: key); @override Widget build(BuildContext context) { - return Navigator( - // observers: [InstabugNavigatorObserver()], + observers: [InstabugNavigatorObserver()], initialRoute: initialRoute, onGenerateRoute: (settings) { final route = routes[settings.name]; if (route == null) return null; //Guard case + // if(exclude.contains(settings.name)) { + // return null ; + // } return MaterialPageRoute( - builder: (context) => InstabugCaptureScreenLoading( - screenName: settings.name ?? "", - child: route.call(context), - ), + builder: (context) { + debugPrint("[RouteWrapper] Screen: ${settings.name} wrapped: "); + return InstabugCaptureScreenLoading( + screenName: settings.name ?? "", + child: route.call(context), + ); + }, settings: settings, ); }, diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/lib/src/utils/screen_loading/screen_loading_manager.dart index a04dae97b..6b25550e9 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -350,18 +350,32 @@ class ScreenLoadingManager { /// Map wrappedRoutes = /// ScreenLoadingAutomaticManager.wrapRoutes( routes) static Map wrapRoutes( - Map routes, - ) { - return { - for (final entry in routes.entries) - entry.key: (BuildContext context) => InstabugCaptureScreenLoading( - screenName: entry.key, - child: entry.value(context), - ), - }; + Map routes, { + List exclude = const [], + }) { + final excludedRoutes = {}; + for (final route in exclude) { + excludedRoutes[route] = true; + } + + final wrappedRoutes = {}; + for (final entry in routes.entries) { + if (!excludedRoutes.containsKey(entry.key)) { + wrappedRoutes[entry.key] = + (BuildContext context) => InstabugCaptureScreenLoading( + screenName: entry.key, + child: entry.value(context), + ); + } else { + wrappedRoutes[entry.key] = entry.value; + } + } + + return wrappedRoutes; } } +@internal class DropScreenLoadingError extends Error { final ScreenLoadingTrace trace; diff --git a/test/utils/screen_loading/route_wrapper_test.dart b/test/utils/screen_loading/route_wrapper_test.dart index 501348f07..794a62008 100644 --- a/test/utils/screen_loading/route_wrapper_test.dart +++ b/test/utils/screen_loading/route_wrapper_test.dart @@ -1,59 +1,59 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:instabug_flutter/instabug_flutter.dart'; -import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; -import 'package:mockito/annotations.dart'; - -import 'route_wrapper_test.mocks.dart'; - - -@GenerateMocks([Placeholder]) -void main() { - - late MockPlaceholder mockWidget; - setUp(() => mockWidget = MockPlaceholder()); - - group('RouteWrapper', () { - testWidgets('wraps routes with InstabugCaptureScreenLoading widgets', - (WidgetTester tester) async { - // Create a map of routes - final routes = { - '/home': (context) => mockWidget, - '/settings': (context) => mockWidget, - }; - - // Create a RouteWrapper widget - final routeWrapper = RouteWrapper( - routes: routes, - child: const MaterialApp(), - ); - - // Pump the widget into the tester - await tester.pumpWidget(routeWrapper); - - // Verify that the routes are wrapped with InstabugCaptureScreenLoading widgets - expect(find.byType(InstabugCaptureScreenLoading), findsWidgets); - }); - - testWidgets('initializes the initial route', (WidgetTester tester) async { - // Create a map of routes - final routes = { - '/home': (context) => mockWidget, - '/settings': (context) => mockWidget, - }; - - // Create a RouteWrapper widget with an initial route - final routeWrapper = RouteWrapper( - routes: routes, - initialRoute: '/settings', - child: const MaterialApp(), - ); - - // Pump the widget into the tester - await tester.pumpWidget(routeWrapper); - - // Verify that the initial route is set correctly - expect(find.byType(MockPlaceholder), findsOneWidget); - }); - }); -} \ No newline at end of file +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:instabug_flutter/instabug_flutter.dart'; +// import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; +// import 'package:mockito/annotations.dart'; +// +// import 'route_wrapper_test.mocks.dart'; +// +// +// @GenerateMocks([Placeholder]) +// void main() { +// +// late MockPlaceholder mockWidget; +// setUp(() => mockWidget = MockPlaceholder()); +// +// group('RouteWrapper', () { +// testWidgets('wraps routes with InstabugCaptureScreenLoading widgets', +// (WidgetTester tester) async { +// // Create a map of routes +// final routes = { +// '/home': (context) => mockWidget, +// '/settings': (context) => mockWidget, +// }; +// +// // Create a RouteWrapper widget +// final routeWrapper = RouteWrapper( +// routes: routes, +// child: const MaterialApp(), +// ); +// +// // Pump the widget into the tester +// await tester.pumpWidget(routeWrapper); +// +// // Verify that the routes are wrapped with InstabugCaptureScreenLoading widgets +// expect(find.byType(InstabugCaptureScreenLoading), findsWidgets); +// }); +// +// testWidgets('initializes the initial route', (WidgetTester tester) async { +// // Create a map of routes +// final routes = { +// '/home': (context) => mockWidget, +// '/settings': (context) => mockWidget, +// }; +// +// // Create a RouteWrapper widget with an initial route +// final routeWrapper = RouteWrapper( +// routes: routes, +// initialRoute: '/settings', +// child: const MaterialApp(), +// ); +// +// // Pump the widget into the tester +// await tester.pumpWidget(routeWrapper); +// +// // Verify that the initial route is set correctly +// expect(find.byType(MockPlaceholder), findsOneWidget); +// }); +// }); +// } \ No newline at end of file From 15418b0995c578b9cf0293ac435aab0ed24bdb93 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Sun, 26 May 2024 15:22:57 +0300 Subject: [PATCH 03/12] feat: add unit test for navigator 1 wrapRoutes method --- .../screen_loading_manager_test.dart | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/test/utils/screen_loading/screen_loading_manager_test.dart index 00d9ed241..4301a9c34 100644 --- a/test/utils/screen_loading/screen_loading_manager_test.dart +++ b/test/utils/screen_loading/screen_loading_manager_test.dart @@ -39,6 +39,7 @@ class ScreenLoadingManagerNoResets extends ScreenLoadingManager { IBGBuildInfo, RouteMatcher, ]) +@GenerateNiceMocks([MockSpec(), MockSpec()]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized(); @@ -51,6 +52,8 @@ void main() { late IBGBuildInfo mIBGBuildInfo; late MockRouteMatcher mRouteMatcher; late InstabugMonotonicClock mInstabugMonotonicClock; + late MockWidget mockScreen; + late MockBuildContext mockBuildContext; const screenName = 'screen1'; setUp(() { @@ -913,4 +916,68 @@ void main() { .called(1); }); }); + + group('wrapRoutes', () { + setUp(() { + mockBuildContext = MockBuildContext(); + mockScreen = MockWidget(); + }); + test('wraps routes with InstabugCaptureScreenLoading widgets', () { + // Create a map of routes + final routes = { + '/home': (context) => mockScreen, + '/settings': (context) => mockScreen, + }; + + // Wrap the routes + final wrappedRoutes = ScreenLoadingManager.wrapRoutes(routes); + + // Verify that the routes are wrapped correctly + expect(wrappedRoutes, isA>()); + expect(wrappedRoutes.length, equals(routes.length)); + for (final route in wrappedRoutes.entries) { + expect(route.value(mockBuildContext), isA()); + } + }); + + test('does not wrap excluded routes', () { + // Create a map of routes + final routes = { + '/home': (context) =>mockScreen, + '/settings': (context) => mockScreen, + }; + + // Exclude the '/home' route + final wrappedRoutes = + ScreenLoadingManager.wrapRoutes(routes, exclude: ['/home']); + + // Verify that the '/home' route is not wrapped + expect(wrappedRoutes['/home'], equals(routes['/home'])); + + // Verify that the '/settings' route is wrapped + expect(wrappedRoutes['/settings']?.call(mockBuildContext), isA()); + }); + + test('handles empty routes map', () { + // Create an empty map of routes + final routes = {}; + + // Wrap the routes + final wrappedRoutes = ScreenLoadingManager.wrapRoutes(routes); + + // Verify that the returned map is empty + expect(wrappedRoutes, isEmpty); + }); + + test('handles null routes map', () { + // Create a null map of routes + Map? routes; + + // Wrap the routes + final wrappedRoutes = ScreenLoadingManager.wrapRoutes(routes ?? {}); + + // Verify that the returned map is empty + expect(wrappedRoutes, isEmpty); + }); + }); } From c9aab812411ea52b646ba588d47aa697519c44b1 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Sun, 26 May 2024 17:30:52 +0300 Subject: [PATCH 04/12] feat: add screen name sanitization unit tests and fix bug in its logic --- .../screen_loading_manager.dart | 2 +- .../screen_loading_manager_test.dart | 40 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/lib/src/utils/screen_loading/screen_loading_manager.dart index eb3563ff6..d18633d30 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -121,7 +121,7 @@ class ScreenLoadingManager { sanitizedScreenName = sanitizedScreenName.substring(1); } if (screenName[lastIndex] == characterToBeRemoved) { - sanitizedScreenName = sanitizedScreenName.substring(0, lastIndex - 1); + sanitizedScreenName = sanitizedScreenName.substring(0, sanitizedScreenName.length - 1); } return sanitizedScreenName; } diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/test/utils/screen_loading/screen_loading_manager_test.dart index 4301a9c34..ea6f028a9 100644 --- a/test/utils/screen_loading/screen_loading_manager_test.dart +++ b/test/utils/screen_loading/screen_loading_manager_test.dart @@ -917,6 +917,38 @@ void main() { }); }); + group('sanitize screen name tests', () { + test('screen name equals to [/] should be replaced bu [ROOT_PAGE]', () { + const screenName = '/'; + final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + expect(sanitizedScreenName, "ROOT_PAGE"); + }); + + test('screen name prefixed with [/] should omit [/] char', () { + const screenName = '/Home'; + final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + expect(sanitizedScreenName, "Home"); + }); + + test('screen name suffixed with [/] should omit [/] char', () { + const screenName = '/Home'; + final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + expect(sanitizedScreenName, "Home"); + }); + + test('screen name without [/] on edges should return the same ', () { + const screenName = 'Home'; + final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + expect(sanitizedScreenName, "Home"); + }); + test('screen name prefixed with [//] and suffixed with [/] should omit first and last[/] char', () { + const screenName = '//Home/'; + final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + expect(sanitizedScreenName, "/Home"); + }); + + }); + group('wrapRoutes', () { setUp(() { mockBuildContext = MockBuildContext(); @@ -936,14 +968,15 @@ void main() { expect(wrappedRoutes, isA>()); expect(wrappedRoutes.length, equals(routes.length)); for (final route in wrappedRoutes.entries) { - expect(route.value(mockBuildContext), isA()); + expect( + route.value(mockBuildContext), isA()); } }); test('does not wrap excluded routes', () { // Create a map of routes final routes = { - '/home': (context) =>mockScreen, + '/home': (context) => mockScreen, '/settings': (context) => mockScreen, }; @@ -955,7 +988,8 @@ void main() { expect(wrappedRoutes['/home'], equals(routes['/home'])); // Verify that the '/settings' route is wrapped - expect(wrappedRoutes['/settings']?.call(mockBuildContext), isA()); + expect(wrappedRoutes['/settings']?.call(mockBuildContext), + isA()); }); test('handles empty routes map', () { From 3d60da77954c95d2bf730fa8a5a391ebe1a464a2 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Mon, 27 May 2024 14:53:45 +0300 Subject: [PATCH 05/12] fix: update example app to capture home page --- example/lib/main.dart | 1 - example/lib/src/app_routes.dart | 4 ++-- lib/src/modules/apm.dart | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 10f4fb4ec..6da763358 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -77,7 +77,6 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } diff --git a/example/lib/src/app_routes.dart b/example/lib/src/app_routes.dart index 1237d6bf1..9175d5405 100644 --- a/example/lib/src/app_routes.dart +++ b/example/lib/src/app_routes.dart @@ -6,8 +6,8 @@ final appRoutes = { /// Home Widget specified in MaterialApp() other wise the the Flutter engine /// will throw a Runtime exception deo to Flutter restrictions - // "/": (BuildContext context) => - // const MyHomePage(title: 'Flutter Demo Home Pag'), + "/": (BuildContext context) => + const MyHomePage(title: 'Flutter Demo Home Pag'), CrashesPage.screenName: (BuildContext context) => const CrashesPage(), ComplexPage.screenName: (BuildContext context) => const ComplexPage(), ApmPage.screenName: (BuildContext context) => const ApmPage(), diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index d2beb1092..40724b85d 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -225,7 +225,6 @@ class APM { Map routes, { List exclude = const [], }) { - final x = ScreenLoadingManager.wrapRoutes(routes, exclude: exclude); - return x; + return ScreenLoadingManager.wrapRoutes(routes, exclude: exclude); } } From f51825d3845f9101b58e2d982150d803dbd2f2ee Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:36:06 +0300 Subject: [PATCH 06/12] chore: remove unused codes & format files --- example/lib/main.dart | 2 +- .../src/components/fatal_crashes_content.dart | 8 +-- .../components/non_fatal_crashes_content.dart | 4 +- example/lib/src/screens/complex_page.dart | 3 +- example/lib/src/screens/crashes_page.dart | 2 +- example/lib/src/screens/my_home_page.dart | 1 - ...reen_capture_premature_extension_page.dart | 2 +- .../lib/src/screens/screen_loading_page.dart | 8 +-- example/lib/src/widget/nested_view.dart | 4 +- lib/src/modules/apm.dart | 2 +- .../utils/screen_loading/route_wrapper.dart | 54 ----------------- .../screen_loading_manager.dart | 1 - .../screen_loading/route_wrapper_test.dart | 59 ------------------- .../screen_loading_manager_test.dart | 1 - 14 files changed, 17 insertions(+), 134 deletions(-) delete mode 100644 lib/src/utils/screen_loading/route_wrapper.dart delete mode 100644 test/utils/screen_loading/route_wrapper_test.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 6da763358..7749ed02d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -72,7 +72,7 @@ class MyApp extends StatelessWidget { navigatorObservers: [ InstabugNavigatorObserver(), ], - routes: APM.wrapRoutes(appRoutes , exclude: [CrashesPage.screenName]), + routes: APM.wrapRoutes(appRoutes, exclude: [CrashesPage.screenName]), theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, diff --git a/example/lib/src/components/fatal_crashes_content.dart b/example/lib/src/components/fatal_crashes_content.dart index c37f7e01c..024f0f193 100644 --- a/example/lib/src/components/fatal_crashes_content.dart +++ b/example/lib/src/components/fatal_crashes_content.dart @@ -61,9 +61,9 @@ class FatalCrashesContent extends StatelessWidget { ), Platform.isAndroid ? const InstabugButton( - text: 'Send Native ANR', - onPressed: InstabugFlutterExampleMethodChannel.sendAnr, - ) + text: 'Send Native ANR', + onPressed: InstabugFlutterExampleMethodChannel.sendAnr, + ) : const SizedBox.shrink(), const InstabugButton( text: 'Throw Unhandled Native OOM Exception', @@ -72,4 +72,4 @@ class FatalCrashesContent extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/example/lib/src/components/non_fatal_crashes_content.dart b/example/lib/src/components/non_fatal_crashes_content.dart index 38aa9160b..1a6fd6cbd 100644 --- a/example/lib/src/components/non_fatal_crashes_content.dart +++ b/example/lib/src/components/non_fatal_crashes_content.dart @@ -58,9 +58,9 @@ class NonFatalCrashesContent extends StatelessWidget { const InstabugButton( text: 'Throw Handled Native Exception', onPressed: - InstabugFlutterExampleMethodChannel.sendNativeNonFatalCrash, + InstabugFlutterExampleMethodChannel.sendNativeNonFatalCrash, ), ], ); } -} \ No newline at end of file +} diff --git a/example/lib/src/screens/complex_page.dart b/example/lib/src/screens/complex_page.dart index 6e1b89498..65fdd8a57 100644 --- a/example/lib/src/screens/complex_page.dart +++ b/example/lib/src/screens/complex_page.dart @@ -43,7 +43,6 @@ class _ComplexPageState extends State { }); } - void _resetDidStartScreenLoading() { ScreenLoadingManager.I.resetDidStartScreenLoading(); } @@ -138,4 +137,4 @@ class _ComplexPageState extends State { ); } } -} \ No newline at end of file +} diff --git a/example/lib/src/screens/crashes_page.dart b/example/lib/src/screens/crashes_page.dart index 421842f57..caa6b7f48 100644 --- a/example/lib/src/screens/crashes_page.dart +++ b/example/lib/src/screens/crashes_page.dart @@ -19,4 +19,4 @@ class CrashesPage extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart index 3163d2642..283a09ecf 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/example/lib/src/screens/my_home_page.dart @@ -129,7 +129,6 @@ class _MyHomePageState extends State { // ); } - void _navigateToApm() { Navigator.push( context, diff --git a/example/lib/src/screens/screen_capture_premature_extension_page.dart b/example/lib/src/screens/screen_capture_premature_extension_page.dart index db7bb8556..befbec341 100644 --- a/example/lib/src/screens/screen_capture_premature_extension_page.dart +++ b/example/lib/src/screens/screen_capture_premature_extension_page.dart @@ -27,4 +27,4 @@ class _ScreenCapturePrematureExtensionPageState ], ); } -} \ No newline at end of file +} diff --git a/example/lib/src/screens/screen_loading_page.dart b/example/lib/src/screens/screen_loading_page.dart index 4d687363a..a2b49e681 100644 --- a/example/lib/src/screens/screen_loading_page.dart +++ b/example/lib/src/screens/screen_loading_page.dart @@ -52,9 +52,9 @@ class _ScreenLoadingPageState extends State { } else { debugPrint( 'Screen loading monitoring is disabled, skipping ending screen loading monitoring with APM.endScreenLoading().\n' - 'Please refer to the documentation for how to enable screen loading monitoring in your app: ' - 'https://docs.instabug.com/docs/flutter-apm-screen-loading#disablingenabling-screen-loading-tracking' - "If Screen Loading is enabled but you're still seeing this message, please reach out to support.", + 'Please refer to the documentation for how to enable screen loading monitoring in your app: ' + 'https://docs.instabug.com/docs/flutter-apm-screen-loading#disablingenabling-screen-loading-tracking' + "If Screen Loading is enabled but you're still seeing this message, please reach out to support.", ); } } @@ -183,4 +183,4 @@ class _ScreenLoadingPageState extends State { ], ); } -} \ No newline at end of file +} diff --git a/example/lib/src/widget/nested_view.dart b/example/lib/src/widget/nested_view.dart index 83401539d..e61099f8c 100644 --- a/example/lib/src/widget/nested_view.dart +++ b/example/lib/src/widget/nested_view.dart @@ -31,7 +31,7 @@ class NestedView extends StatelessWidget { Row( children: List.generate( breadth, - (index) => NestedView( + (index) => NestedView( depth: depth - 1, breadth: breadth, child: child, @@ -42,4 +42,4 @@ class NestedView extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/modules/apm.dart b/lib/src/modules/apm.dart index 28a061266..70f7bf6b0 100644 --- a/lib/src/modules/apm.dart +++ b/lib/src/modules/apm.dart @@ -2,7 +2,7 @@ import 'dart:async'; -import 'package:flutter/widgets.dart' show WidgetBuilder, debugPrint; +import 'package:flutter/widgets.dart' show WidgetBuilder; import 'package:instabug_flutter/src/generated/apm.api.g.dart'; import 'package:instabug_flutter/src/models/network_data.dart'; import 'package:instabug_flutter/src/models/trace.dart'; diff --git a/lib/src/utils/screen_loading/route_wrapper.dart b/lib/src/utils/screen_loading/route_wrapper.dart deleted file mode 100644 index 8ccd2db3e..000000000 --- a/lib/src/utils/screen_loading/route_wrapper.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:instabug_flutter/instabug_flutter.dart'; - -/// A widget that wraps the app's routes with [InstabugCaptureScreenLoading] widgets. -/// -/// This allows Instabug to automatically capture screen loading times. -class RouteWrapper extends StatelessWidget { - /// The child widget to wrap. - final Widget child; - - /// A map of routes to wrap. - final Map routes; - - /// The initial route to navigate to. - final String? initialRoute; - - final List exclude; - - /// Creates a new instance of [RouteWrapper]. - const RouteWrapper( - {Key? key, - required this.child, - required this.routes, - this.initialRoute, - this.exclude = const []}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return Navigator( - observers: [InstabugNavigatorObserver()], - initialRoute: initialRoute, - onGenerateRoute: (settings) { - final route = routes[settings.name]; - - if (route == null) return null; //Guard case - - // if(exclude.contains(settings.name)) { - // return null ; - // } - return MaterialPageRoute( - builder: (context) { - debugPrint("[RouteWrapper] Screen: ${settings.name} wrapped: "); - return InstabugCaptureScreenLoading( - screenName: settings.name ?? "", - child: route.call(context), - ); - }, - settings: settings, - ); - }, - ); - } -} diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/lib/src/utils/screen_loading/screen_loading_manager.dart index 85d5d01d8..8d393e762 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -5,7 +5,6 @@ import 'package:instabug_flutter/src/utils/ibg_date_time.dart'; import 'package:instabug_flutter/src/utils/instabug_logger.dart'; import 'package:instabug_flutter/src/utils/instabug_montonic_clock.dart'; import 'package:instabug_flutter/src/utils/screen_loading/flags_config.dart'; -import 'package:instabug_flutter/src/utils/screen_loading/route_matcher.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_trace.dart'; import 'package:instabug_flutter/src/utils/screen_loading/ui_trace.dart'; import 'package:meta/meta.dart'; diff --git a/test/utils/screen_loading/route_wrapper_test.dart b/test/utils/screen_loading/route_wrapper_test.dart deleted file mode 100644 index 794a62008..000000000 --- a/test/utils/screen_loading/route_wrapper_test.dart +++ /dev/null @@ -1,59 +0,0 @@ -// import 'package:flutter/material.dart'; -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:instabug_flutter/instabug_flutter.dart'; -// import 'package:instabug_flutter/src/utils/screen_loading/route_wrapper.dart'; -// import 'package:mockito/annotations.dart'; -// -// import 'route_wrapper_test.mocks.dart'; -// -// -// @GenerateMocks([Placeholder]) -// void main() { -// -// late MockPlaceholder mockWidget; -// setUp(() => mockWidget = MockPlaceholder()); -// -// group('RouteWrapper', () { -// testWidgets('wraps routes with InstabugCaptureScreenLoading widgets', -// (WidgetTester tester) async { -// // Create a map of routes -// final routes = { -// '/home': (context) => mockWidget, -// '/settings': (context) => mockWidget, -// }; -// -// // Create a RouteWrapper widget -// final routeWrapper = RouteWrapper( -// routes: routes, -// child: const MaterialApp(), -// ); -// -// // Pump the widget into the tester -// await tester.pumpWidget(routeWrapper); -// -// // Verify that the routes are wrapped with InstabugCaptureScreenLoading widgets -// expect(find.byType(InstabugCaptureScreenLoading), findsWidgets); -// }); -// -// testWidgets('initializes the initial route', (WidgetTester tester) async { -// // Create a map of routes -// final routes = { -// '/home': (context) => mockWidget, -// '/settings': (context) => mockWidget, -// }; -// -// // Create a RouteWrapper widget with an initial route -// final routeWrapper = RouteWrapper( -// routes: routes, -// initialRoute: '/settings', -// child: const MaterialApp(), -// ); -// -// // Pump the widget into the tester -// await tester.pumpWidget(routeWrapper); -// -// // Verify that the initial route is set correctly -// expect(find.byType(MockPlaceholder), findsOneWidget); -// }); -// }); -// } \ No newline at end of file diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/test/utils/screen_loading/screen_loading_manager_test.dart index 2d6586a58..12f51e1e7 100644 --- a/test/utils/screen_loading/screen_loading_manager_test.dart +++ b/test/utils/screen_loading/screen_loading_manager_test.dart @@ -8,7 +8,6 @@ import 'package:instabug_flutter/src/utils/ibg_date_time.dart'; import 'package:instabug_flutter/src/utils/instabug_logger.dart'; import 'package:instabug_flutter/src/utils/instabug_montonic_clock.dart'; import 'package:instabug_flutter/src/utils/screen_loading/flags_config.dart'; -import 'package:instabug_flutter/src/utils/screen_loading/route_matcher.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_trace.dart'; import 'package:instabug_flutter/src/utils/screen_loading/ui_trace.dart'; From 3f73dfe53ca75921c292e310ac1c4e41fc77e585 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 4 Jun 2024 12:25:19 +0300 Subject: [PATCH 07/12] chore(android): bump SDK to v13.1.1 --- CHANGELOG.md | 199 +++++++++++++++++++++++++++++-------------- android/build.gradle | 2 +- 2 files changed, 136 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cebed1910..011274753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,131 +1,186 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.0...dev) + +### Changed + +- Bump Instabug Android SDK to v13.1.1 ([#473](https://github.com/Instabug/Instabug-Flutter/pull/473)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.1.1). + + ## [13.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.7.0...dev) (April 29, 2024) ### Added - Adds custom app rating api ([#453](https://github.com/Instabug/Instabug-Flutter/pull/453)) -- Add `SessionReplay.getSessionReplayLink` API which retrieves the current session's replay link ([#445](https://github.com/Instabug/Instabug-Flutter/pull/445)). -- Add support for App Flows APIs `APM.startFlow`, `APM.endFlow` and `APM.setFlowAttribute` ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). +- Add `SessionReplay.getSessionReplayLink` API which retrieves the current session's replay + link ([#445](https://github.com/Instabug/Instabug-Flutter/pull/445)). +- Add support for App Flows APIs `APM.startFlow`, `APM.endFlow` + and `APM.setFlowAttribute` ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). ### Deprecated -- Deprecate execution traces APIs `APM.startExecutionTrace`, `APM.setExecutionTraceAttribute`, `APM.endExecutionTrace`, `Trace.setAttribute` and `Trace.end` in favor of the new app flow APIs ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). +- Deprecate execution traces + APIs `APM.startExecutionTrace`, `APM.setExecutionTraceAttribute`, `APM.endExecutionTrace`, `Trace.setAttribute` + and `Trace.end` in favor of the new app flow + APIs ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). ### Changed -- Bump Instabug Android SDK to v13.0.0 ([#455](https://github.com/Instabug/Instabug-Flutter/pull/455)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.0.0). -- Bump Instabug iOS SDK to v13.0.0 ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.0.0). + +- Bump Instabug Android SDK to + v13.0.0 ([#455](https://github.com/Instabug/Instabug-Flutter/pull/455)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.0.0). +- Bump Instabug iOS SDK to + v13.0.0 ([#446](https://github.com/Instabug/Instabug-Flutter/pull/446)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.0.0). ## [12.7.0](https://github.com/Instabug/Instabug-Flutter/compare/v12.5.0...v12.7.0) (February 15, 2024) ### Added -- Support user identification using ID ([#435](https://github.com/Instabug/Instabug-Flutter/pull/435)). +- Support user identification using + ID ([#435](https://github.com/Instabug/Instabug-Flutter/pull/435)). ### Changed -- Bump Instabug iOS SDK to v12.7.0 ([#440](https://github.com/Instabug/Instabug-Flutter/pull/440)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/12.7.0). -- Bump Instabug Android SDK to v12.7.1 ([#439](https://github.com/Instabug/Instabug-Flutter/pull/439)). See release notes for [v12.7.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.7.1). +- Bump Instabug iOS SDK to + v12.7.0 ([#440](https://github.com/Instabug/Instabug-Flutter/pull/440)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/12.7.0). +- Bump Instabug Android SDK to + v12.7.1 ([#439](https://github.com/Instabug/Instabug-Flutter/pull/439)). See release notes + for [v12.7.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.7.1). ## [12.5.0](https://github.com/Instabug/Instabug-Flutter/compare/v12.4.0...v12.5.0) (January 08 , 2024) ### Changed -- Bump Instabug iOS SDK to v12.5.0 ([#425](https://github.com/Instabug/Instabug-Flutter/pull/425)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/12.5.0). -- Bump Instabug Android SDK to v12.5.1 ([#426](https://github.com/Instabug/Instabug-Flutter/pull/426)). See release notes for [v12.5.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.5.0) and [v12.5.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.5.1). +- Bump Instabug iOS SDK to + v12.5.0 ([#425](https://github.com/Instabug/Instabug-Flutter/pull/425)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/12.5.0). +- Bump Instabug Android SDK to + v12.5.1 ([#426](https://github.com/Instabug/Instabug-Flutter/pull/426)). See release notes + for [v12.5.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.5.0) + and [v12.5.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.5.1). ## [12.4.0](https://github.com/Instabug/Instabug-Flutter/compare/v12.2.0...v12.4.0) (December 13, 2023) ### Changed -- Bump Instabug iOS SDK to v12.4.0 ([#419](https://github.com/Instabug/Instabug-Flutter/pull/419)). See release notes for [v12.3.0](https://github.com/instabug/instabug-ios/releases/tag/12.3.0) and [v12.4.0](https://github.com/instabug/instabug-ios/releases/tag/12.4.0). -- Bump Instabug Android SDK to v12.4.1 ([#420](https://github.com/Instabug/Instabug-Flutter/pull/420)). See release notes for [v12.3.0](https://github.com/Instabug/android/releases/tag/v12.3.0), [v12.3.1](https://github.com/Instabug/android/releases/tag/v12.3.1), [v12.4.0](https://github.com/Instabug/android/releases/tag/v12.4.0) and [v12.4.1](https://github.com/Instabug/android/releases/tag/v12.4.1). +- Bump Instabug iOS SDK to v12.4.0 ([#419](https://github.com/Instabug/Instabug-Flutter/pull/419)). + See release notes for [v12.3.0](https://github.com/instabug/instabug-ios/releases/tag/12.3.0) + and [v12.4.0](https://github.com/instabug/instabug-ios/releases/tag/12.4.0). +- Bump Instabug Android SDK to + v12.4.1 ([#420](https://github.com/Instabug/Instabug-Flutter/pull/420)). See release notes + for [v12.3.0](https://github.com/Instabug/android/releases/tag/v12.3.0), [v12.3.1](https://github.com/Instabug/android/releases/tag/v12.3.1), [v12.4.0](https://github.com/Instabug/android/releases/tag/v12.4.0) + and [v12.4.1](https://github.com/Instabug/android/releases/tag/v12.4.1). ## [12.2.0](https://github.com/Instabug/Instabug-Flutter/compare/12.1.0...12.2.0) (November 16, 2023) ### Changed -- Bump Instabug iOS SDK to v12.2.0 ([#406](https://github.com/Instabug/Instabug-Flutter/pull/406)). [See release notes](https://github.com/instabug/instabug-ios/releases/tag/12.2.0). -- Bump Instabug Android SDK to v12.2.0 ([#405](https://github.com/Instabug/Instabug-Flutter/pull/405)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v12.2.0). +- Bump Instabug iOS SDK to + v12.2.0 ([#406](https://github.com/Instabug/Instabug-Flutter/pull/406)). [See release notes](https://github.com/instabug/instabug-ios/releases/tag/12.2.0). +- Bump Instabug Android SDK to + v12.2.0 ([#405](https://github.com/Instabug/Instabug-Flutter/pull/405)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v12.2.0). ### Fixed -- Re-enable screenshot capturing for Crash Reporting and Session Replay by removing redundant mapping ([#407](https://github.com/Instabug/Instabug-Flutter/pull/407)). +- Re-enable screenshot capturing for Crash Reporting and Session Replay by removing redundant + mapping ([#407](https://github.com/Instabug/Instabug-Flutter/pull/407)). ## [12.1.0](https://github.com/Instabug/Instabug-Flutter/compare/v11.14.0...v12.1.0) (September 28, 2023) ### Added -- Add support for Session Replay, which includes capturing session details, visual reproduction of sessions as well as support for user steps, network and Instabug logs. ([#395](https://github.com/Instabug/Instabug-Flutter/pull/395)). +- Add support for Session Replay, which includes capturing session details, visual reproduction of + sessions as well as support for user steps, network and Instabug + logs. ([#395](https://github.com/Instabug/Instabug-Flutter/pull/395)). ### Changed -- **BREAKING** Remove deprecated APIs ([#385](https://github.com/Instabug/Instabug-Flutter/pull/385)). See migration guide for more details. -- Bump Instabug iOS SDK to v12.1.0 ([#396](https://github.com/Instabug/Instabug-Flutter/pull/396)). See release notes for [v12.0.0](https://github.com/instabug/instabug-ios/releases/tag/12.0.0) and [v12.1.0](https://github.com/instabug/instabug-ios/releases/tag/12.1.0). -- Bump Instabug Android SDK to v12.1.0 ([#397](https://github.com/Instabug/Instabug-Flutter/pull/397)). See release notes for [v12.0.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.0.0), [v12.0.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.0.1) and [v12.1.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.1.0). +- **BREAKING** Remove deprecated + APIs ([#385](https://github.com/Instabug/Instabug-Flutter/pull/385)). See migration guide for more + details. +- Bump Instabug iOS SDK to v12.1.0 ([#396](https://github.com/Instabug/Instabug-Flutter/pull/396)). + See release notes for [v12.0.0](https://github.com/instabug/instabug-ios/releases/tag/12.0.0) + and [v12.1.0](https://github.com/instabug/instabug-ios/releases/tag/12.1.0). +- Bump Instabug Android SDK to + v12.1.0 ([#397](https://github.com/Instabug/Instabug-Flutter/pull/397)). See release notes + for [v12.0.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.0.0), [v12.0.1](https://github.com/Instabug/Instabug-Android/releases/tag/v12.0.1) + and [v12.1.0](https://github.com/Instabug/Instabug-Android/releases/tag/v12.1.0). ## [11.14.0](https://github.com/Instabug/Instabug-Flutter/compare/v11.13.0...v11.14.0) (September 13, 2023) ### Added -- Add network logs obfuscation support using the new `NetworkLogger.obfuscateLog` API ([#380](https://github.com/Instabug/Instabug-Flutter/pull/380)). -- Add network logs omission support using the new `NetworkLogger.omitLog` API ([#382](https://github.com/Instabug/Instabug-Flutter/pull/382)). -- Add the new repro steps configuration API `Instabug.setReproStepsConfig` ([#388](https://github.com/Instabug/Instabug-Flutter/pull/388)). +- Add network logs obfuscation support using the new `NetworkLogger.obfuscateLog` + API ([#380](https://github.com/Instabug/Instabug-Flutter/pull/380)). +- Add network logs omission support using the new `NetworkLogger.omitLog` + API ([#382](https://github.com/Instabug/Instabug-Flutter/pull/382)). +- Add the new repro steps configuration + API `Instabug.setReproStepsConfig` ([#388](https://github.com/Instabug/Instabug-Flutter/pull/388)). ### Changed -- Bump Instabug Android SDK to v11.14.0 ([#384](https://github.com/Instabug/Instabug-Flutter/pull/384)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.14.0). -- Bump Instabug iOS SDK to v11.14.0 ([#383](https://github.com/Instabug/Instabug-Flutter/pull/383)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.14.0). +- Bump Instabug Android SDK to + v11.14.0 ([#384](https://github.com/Instabug/Instabug-Flutter/pull/384)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.14.0). +- Bump Instabug iOS SDK to + v11.14.0 ([#383](https://github.com/Instabug/Instabug-Flutter/pull/383)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.14.0). ### Deprecated -- Deprecate `Instabug.setReproStepsMode` in favor of the new `Instabug.setReproStepsConfig` ([#388](https://github.com/Instabug/Instabug-Flutter/pull/388)). +- Deprecate `Instabug.setReproStepsMode` in favor of the + new `Instabug.setReproStepsConfig` ([#388](https://github.com/Instabug/Instabug-Flutter/pull/388)). ## [11.13.0](https://github.com/Instabug/Instabug-Flutter/compare/v11.12.0...v11.13.0) (July 10, 2023) ### Changed -- Bump Instabug iOS SDK to v11.13.3 ([#373](https://github.com/Instabug/Instabug-Flutter/pull/373)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/v11.13.0). -- Bump Instabug Android SDK to v11.13.0 ([#372](https://github.com/Instabug/Instabug-Flutter/pull/372)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.13.0). +- Bump Instabug iOS SDK to + v11.13.3 ([#373](https://github.com/Instabug/Instabug-Flutter/pull/373)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/v11.13.0). +- Bump Instabug Android SDK to + v11.13.0 ([#372](https://github.com/Instabug/Instabug-Flutter/pull/372)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.13.0). ### Fixed -- Fix an issue that caused APIs that return a value or invoke a callback break on Android in some versions of Flutter ([#370](https://github.com/Instabug/Instabug-Flutter/pull/370), [#369](https://github.com/Instabug/Instabug-Flutter/pull/369)). +- Fix an issue that caused APIs that return a value or invoke a callback break on Android in some + versions of + Flutter ([#370](https://github.com/Instabug/Instabug-Flutter/pull/370), [#369](https://github.com/Instabug/Instabug-Flutter/pull/369)). Below is a list of all the affected APIs: - - `APM.startExecutionTrace` - - `BugReporting.setOnInvokeCallback` - - `BugReporting.setOnDismissCallback` - - `Instabug.getTags` - - `Instabug.getUserAttributeForKey` - - `Instabug.getUserAttributes` - - `Replies.getUnreadRepliesCount` - - `Replies.hasChats` - - `Replies.setOnNewReplyReceivedCallback` - - `Surveys.hasRespondToSurvey` - - `Surveys.setOnShowCallback` - - `Surveys.setOnDismissCallback` + - `APM.startExecutionTrace` + - `BugReporting.setOnInvokeCallback` + - `BugReporting.setOnDismissCallback` + - `Instabug.getTags` + - `Instabug.getUserAttributeForKey` + - `Instabug.getUserAttributes` + - `Replies.getUnreadRepliesCount` + - `Replies.hasChats` + - `Replies.setOnNewReplyReceivedCallback` + - `Surveys.hasRespondToSurvey` + - `Surveys.setOnShowCallback` + - `Surveys.setOnDismissCallback` ## [11.12.0](https://github.com/Instabug/Instabug-Flutter/compare/v11.10.1...v11.12.0) (May 30, 2023) ### Changed -- Bump Instabug Android SDK to v11.12.0 ([#366](https://github.com/Instabug/Instabug-Flutter/pull/366)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.12.0). -- Bump Instabug iOS SDK to v11.12.0 ([#365](https://github.com/Instabug/Instabug-Flutter/pull/365)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.12.0). +- Bump Instabug Android SDK to + v11.12.0 ([#366](https://github.com/Instabug/Instabug-Flutter/pull/366)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.12.0). +- Bump Instabug iOS SDK to + v11.12.0 ([#365](https://github.com/Instabug/Instabug-Flutter/pull/365)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.12.0). ## [11.10.1](https://github.com/Instabug/Instabug-Flutter/compare/v11.10.0...v11.10.1) (April 17, 2023) ### Changed -- Bump Instabug iOS SDK to v11.10.1 ([#358](https://github.com/Instabug/Instabug-Flutter/pull/358)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.10.1). +- Bump Instabug iOS SDK to + v11.10.1 ([#358](https://github.com/Instabug/Instabug-Flutter/pull/358)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.10.1). ## [11.10.0](https://github.com/Instabug/Instabug-Flutter/compare/v11.9.0...v11.10.0) (April 12, 2023) ### Changed -- Bump Instabug Android SDK to v11.11.0 ([#352](https://github.com/Instabug/Instabug-Flutter/pull/352)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.11.0). -- Bump Instabug iOS SDK to v11.10.0 ([#353](https://github.com/Instabug/Instabug-Flutter/pull/353)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.10.0). +- Bump Instabug Android SDK to + v11.11.0 ([#352](https://github.com/Instabug/Instabug-Flutter/pull/352)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v11.11.0). +- Bump Instabug iOS SDK to + v11.10.0 ([#353](https://github.com/Instabug/Instabug-Flutter/pull/353)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/11.10.0). ## 11.9.0 (2023-02-21) @@ -141,7 +196,8 @@ ``` - Adds `hungarian` and `finnish` locales support. - Deprecates `Instabug.start` in favour of `Instabug.init`. -- Deprecates `Instabug.setDebugEnabled`, `Instabug.setSdkDebugLogsLevel`, and `APM.setLogLevel` in favour of `debugLogsLevel` parameter of `Instabug.init`. +- Deprecates `Instabug.setDebugEnabled`, `Instabug.setSdkDebugLogsLevel`, and `APM.setLogLevel` in + favour of `debugLogsLevel` parameter of `Instabug.init`. - Deprecates the `IBGSDKDebugLogsLevel` enum in favour of the `LogLevel` enum. - Deprecates both `warning` and `info` values from the `LogLevel` enum. - Fixes `norwegian` and `slovak` locales on iOS. @@ -152,7 +208,8 @@ - Bumps Instabug Android SDK to v11.7.0 - Bumps Instabug iOS SDK to v11.6.0 -- Adds new string keys: okButtonText, audio, image, screenRecording, messagesNotificationAndOthers, insufficientContentTitle, insufficientContentMessage +- Adds new string keys: okButtonText, audio, image, screenRecording, messagesNotificationAndOthers, + insufficientContentTitle, insufficientContentMessage - Fixes APM network logging on Android - Fixes a NullPointerException when overriding a string key that doesn't exist on Android - Removes redundant native logs @@ -168,14 +225,17 @@ - Removes "Media Projection" dialog while taking screenshots on Android - Fixes APM network logging on Android - Fixes main thread violation on Android -- Fixes an issue with request and response headers parameters type causing network requests not getting logged on iOS -- Improves performance by using pigeon for internal communication between Flutter and the host platform -- Deprecates Instabug.enableAndroid and Instabug.disableAndroid APIs in favour of the new API Instabug.setEnabled, which works on both platforms +- Fixes an issue with request and response headers parameters type causing network requests not + getting logged on iOS +- Improves performance by using pigeon for internal communication between Flutter and the host + platform +- Deprecates Instabug.enableAndroid and Instabug.disableAndroid APIs in favour of the new API + Instabug.setEnabled, which works on both platforms - Deprecates callbacks in favor of return values in the following APIs: - 1. Replies.getUnreadRepliesCount - 2. Replies.hasChats - 3. Surveys.hasRespondedToSurvey - 4. Surveys.getAvailableSurveys + 1. Replies.getUnreadRepliesCount + 2. Replies.hasChats + 3. Surveys.hasRespondedToSurvey + 4. Surveys.getAvailableSurveys ## 11.3.0 (2022-09-30) @@ -191,19 +251,24 @@ - Bumps Instabug Android SDK to v11.4.1 - Bumps Instabug iOS SDK to v11.2.0 - Fixes an issue with BugReporting.setInvocationEvents on iOS that always sets the event to none -- Fixes an issue with network logging on iOS which caused the initial network requests logs to be skipped +- Fixes an issue with network logging on iOS which caused the initial network requests logs to be + skipped - Renames Android package from com.instabug.instabugFlutter to com.instabug.flutter ## v11.0.0 (2022-07-20) - Bumps Instabug native SDKs to v11 -- Adds the ability to initialize the Android SDK from Dart. Check the migration guide referenced in our docs -- Changes the package importing style for a more conventional use. Check the migration guide referenced in our docs -- Moves InstabugCustomHttpClient used for network logging into a separate repo. Check the migration guide referenced in our docs +- Adds the ability to initialize the Android SDK from Dart. Check the migration guide referenced in + our docs +- Changes the package importing style for a more conventional use. Check the migration guide + referenced in our docs +- Moves InstabugCustomHttpClient used for network logging into a separate repo. Check the migration + guide referenced in our docs - Flutter 3 compatibility - Bumps Gradle to 6.8 & Android Gradle plugin to 4.1 - Adds BugReporting.setFloatingButtonEdge API -- Removes the string keys bugReportHeader and feedbackReportHeader. Check the migration guide referenced in our docs +- Removes the string keys bugReportHeader and feedbackReportHeader. Check the migration guide + referenced in our docs - Removes the deprecated APIs. Check the migration guide referenced in our docs - Fixes an issue with Android screenshots being black on release mode on SDK v10.13.0 @@ -257,7 +322,8 @@ ## v9.1.9 (2021-05-11) -- Adds support for overriding the replies notification string values through `repliesNotificationTeamName`, `repliesNotificationReplyButton`, `repliesNotificationDismissButton` +- Adds support for overriding the replies notification string values + through `repliesNotificationTeamName`, `repliesNotificationReplyButton`, `repliesNotificationDismissButton` - Removes the use of `android:requestLegacyExternalStorage` attribute on Android ## v9.1.8 (2021-02-17) @@ -274,8 +340,10 @@ ## v9.1.6 (2020-07-13) - Added CrashReporting -- Added setShakingThresholdForiPhone, setShakingThresholdForiPad and setShakingThresholdForAndroid APIs -- Added Proguard rules to protect Flutter bridge class and method names from getting obfuscated when the minifyEnabled flag is set to true. +- Added setShakingThresholdForiPhone, setShakingThresholdForiPad and setShakingThresholdForAndroid + APIs +- Added Proguard rules to protect Flutter bridge class and method names from getting obfuscated when + the minifyEnabled flag is set to true. ## v9.1.0 (2020-03-19) @@ -291,7 +359,9 @@ ## Version 9.0.1 (2019-12-12) -- Added enum `CustomTextPlaceHolderKey.reportQuestion` which maps to `InstabugCustomTextPlaceHolder.Key.REPORT_QUESTION` on Android and `kIBGAskAQuestionStringName` on iOS +- Added enum `CustomTextPlaceHolderKey.reportQuestion` which maps + to `InstabugCustomTextPlaceHolder.Key.REPORT_QUESTION` on Android and `kIBGAskAQuestionStringName` + on iOS ## Version 9.0.0 (2019-12-09) @@ -316,7 +386,8 @@ ## Version 1.0.0 (2019-07-29) -**⚠️ Package on pub has been renamed to `instabug_flutter` the old package `instabug` is deprecated** +**⚠️ Package on pub has been renamed to `instabug_flutter` the old package `instabug` is deprecated +** ## Version 1.0.0-beta.5 (2019-07-22) diff --git a/android/build.gradle b/android/build.gradle index 9afbfed05..f5c7a1a49 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,7 +41,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:13.0.0' + api 'com.instabug.library:instabug:13.1.1' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" From 4d44acddcdb952927be6f143a2b5cdd6411426f7 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 4 Jun 2024 14:26:16 +0300 Subject: [PATCH 08/12] docs: fix changeLog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 011274753..0f3b38346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,13 @@ # Changelog -## [Unreleased](https://github.com/Instabug/Instabug-React-Native/compare/v13.0.0...dev) +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v13.0.0...dev) ### Changed -- Bump Instabug Android SDK to v13.1.1 ([#473](https://github.com/Instabug/Instabug-Flutter/pull/473)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.1.1). +- Bump Instabug Android SDK to v13.1.1 ([#474](https://github.com/Instabug/Instabug-Flutter/pull/474)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.1.1). -## [13.0.0](https://github.com/Instabug/Instabug-React-Native/compare/v12.7.0...dev) (April 29, 2024) +## [13.0.0](https://github.com/Instabug/Instabug-Flutter/compare/v12.7.0...dev) (April 29, 2024) ### Added From 562b0f147e8c5526a92c669bd80aaefd267c4cc3 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:06:20 +0300 Subject: [PATCH 09/12] chore(iOS): bump sdk to v13.1.0 (#473) * chore(ios): bump SDK to v13.1.0 * chore(ios): bump SDK to v13.1.0 * docs: fix changeLog --- CHANGELOG.md | 1 + example/ios/Podfile.lock | 8 ++++---- ios/instabug_flutter.podspec | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f3b38346..a59302235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changed +- Bump Instabug iOS SDK to v13.1.0 ([#1227](https://github.com/Instabug/Instabug-Flutter/pull/1227)). [See release notes](https://github.com/Instabug/Instabug-iOS/releases/tag/13.1.0). - Bump Instabug Android SDK to v13.1.1 ([#474](https://github.com/Instabug/Instabug-Flutter/pull/474)). [See release notes](https://github.com/Instabug/Instabug-Android/releases/tag/v13.1.1). diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 4a9cac97e..00201cfdc 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Instabug (13.0.0) + - Instabug (13.1.0) - instabug_flutter (13.0.0): - Flutter - - Instabug (= 13.0.0) + - Instabug (= 13.1.0) - OCMock (3.6) DEPENDENCIES: @@ -24,8 +24,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: fa52de4a6cac26cde0a60ec5e0540f2461a06fe2 - instabug_flutter: b80c4b8748d1da660a8f0cc0b2e5f4375898761c + Instabug: 3d55eff7ea55adf22df404908a2b954b8b585c29 + instabug_flutter: 8b86ee14635a4b0ebfb4f760a108c7b0606c47e4 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 PODFILE CHECKSUM: 637e800c0a0982493b68adb612d2dd60c15c8e5c diff --git a/ios/instabug_flutter.podspec b/ios/instabug_flutter.podspec index 2f54e05da..ae1ebfaa4 100644 --- a/ios/instabug_flutter.podspec +++ b/ios/instabug_flutter.podspec @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-framework "Flutter" -framework "Instabug"'} s.dependency 'Flutter' - s.dependency 'Instabug', '13.0.0' + s.dependency 'Instabug', '13.1.0' end From f4ac87f7fc810c4dc96be791ba9a4e76a83bdab5 Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:02:38 +0300 Subject: [PATCH 10/12] chore: run dart fix and flutter format --- .../screen_loading_manager.dart | 3 +- .../screen_loading_manager_test.dart | 40 ++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/lib/src/utils/screen_loading/screen_loading_manager.dart index 0c8cb66a3..2af57f09f 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -120,7 +120,8 @@ class ScreenLoadingManager { sanitizedScreenName = sanitizedScreenName.substring(1); } if (screenName[lastIndex] == characterToBeRemoved) { - sanitizedScreenName = sanitizedScreenName.substring(0, sanitizedScreenName.length - 1); + sanitizedScreenName = + sanitizedScreenName.substring(0, sanitizedScreenName.length - 1); } return sanitizedScreenName; } diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/test/utils/screen_loading/screen_loading_manager_test.dart index 9fc310781..28ebee30f 100644 --- a/test/utils/screen_loading/screen_loading_manager_test.dart +++ b/test/utils/screen_loading/screen_loading_manager_test.dart @@ -922,34 +922,40 @@ void main() { group('sanitize screen name tests', () { test('screen name equals to [/] should be replaced bu [ROOT_PAGE]', () { - const screenName = '/'; - final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + const screenName = '/'; + final sanitizedScreenName = + ScreenLoadingManager.I.sanitizeScreenName(screenName); expect(sanitizedScreenName, "ROOT_PAGE"); }); test('screen name prefixed with [/] should omit [/] char', () { - const screenName = '/Home'; - final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + const screenName = '/Home'; + final sanitizedScreenName = + ScreenLoadingManager.I.sanitizeScreenName(screenName); expect(sanitizedScreenName, "Home"); }); test('screen name suffixed with [/] should omit [/] char', () { - const screenName = '/Home'; - final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + const screenName = '/Home'; + final sanitizedScreenName = + ScreenLoadingManager.I.sanitizeScreenName(screenName); expect(sanitizedScreenName, "Home"); }); test('screen name without [/] on edges should return the same ', () { - const screenName = 'Home'; - final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + const screenName = 'Home'; + final sanitizedScreenName = + ScreenLoadingManager.I.sanitizeScreenName(screenName); expect(sanitizedScreenName, "Home"); }); - test('screen name prefixed with [//] and suffixed with [/] should omit first and last[/] char', () { - const screenName = '//Home/'; - final sanitizedScreenName = ScreenLoadingManager.I.sanitizeScreenName(screenName); + test( + 'screen name prefixed with [//] and suffixed with [/] should omit first and last[/] char', + () { + const screenName = '//Home/'; + final sanitizedScreenName = + ScreenLoadingManager.I.sanitizeScreenName(screenName); expect(sanitizedScreenName, "/Home"); }); - }); group('wrapRoutes', () { @@ -972,7 +978,9 @@ void main() { expect(wrappedRoutes.length, equals(routes.length)); for (final route in wrappedRoutes.entries) { expect( - route.value(mockBuildContext), isA()); + route.value(mockBuildContext), + isA(), + ); } }); @@ -991,8 +999,10 @@ void main() { expect(wrappedRoutes['/home'], equals(routes['/home'])); // Verify that the '/settings' route is wrapped - expect(wrappedRoutes['/settings']?.call(mockBuildContext), - isA()); + expect( + wrappedRoutes['/settings']?.call(mockBuildContext), + isA(), + ); }); test('handles empty routes map', () { From 5d7cda21babba9c7ace544377cee98b30365bfae Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:02:38 +0300 Subject: [PATCH 11/12] chore: pull ci changes from dev --- .circleci/config.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3c15b00bc..cfba4fee2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -146,8 +146,6 @@ jobs: steps: - advanced-checkout/shallow-checkout - setup_flutter - - android/change-java-version: - java-version: 8 - android/run-tests: working-directory: example/android test-command: ./gradlew test @@ -162,8 +160,6 @@ jobs: - setup_captain: platform: android - setup_flutter - - android/change-java-version: - java-version: 8 - android/start-emulator-and-run-tests: run-tests-working-directory: e2e additional-avd-args: --device 3 From 0615009f7c8282db5975f96f65a522a3f1130d2c Mon Sep 17 00:00:00 2001 From: Andrew Amin <160974398+AndrewAminInstabug@users.noreply.github.com> Date: Sun, 9 Jun 2024 13:27:13 +0300 Subject: [PATCH 12/12] fix: screen_loading_manager_test to pass flutter 2 --- test/utils/screen_loading/screen_loading_manager_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/test/utils/screen_loading/screen_loading_manager_test.dart index 28ebee30f..14e33c663 100644 --- a/test/utils/screen_loading/screen_loading_manager_test.dart +++ b/test/utils/screen_loading/screen_loading_manager_test.dart @@ -36,8 +36,9 @@ class ScreenLoadingManagerNoResets extends ScreenLoadingManager { InstabugMonotonicClock, IBGBuildInfo, RouteMatcher, + BuildContext, + Widget, ]) -@GenerateNiceMocks([MockSpec(), MockSpec()]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();