fix: improve crash reporting on Codeberg
- Pre-fill Codeberg issue with crash details (title and body). - Remove unreliable canLaunchUrl check. - Add SnackBar error handling if launch fails. - Add https intent to Android queries manifest for better link visibility. - Add widget test for CrashScreen.
This commit is contained in:
@@ -38,6 +38,10 @@
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
|
||||
@@ -90,11 +90,33 @@ class CrashScreen extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final url = Uri.parse(
|
||||
'https://codeberg.org/guettli/sharedinbox/issues/new',
|
||||
final title = Uri.encodeComponent(
|
||||
'Crash: ${exception.toString().split('\n').first}',
|
||||
);
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
||||
final body = Uri.encodeComponent(
|
||||
'Error: $exception\n\nStack Trace:\n$stackTrace',
|
||||
);
|
||||
final url = Uri.parse(
|
||||
'https://codeberg.org/guettli/sharedinbox/issues/new?title=$title&body=$body',
|
||||
);
|
||||
try {
|
||||
final launched = await launchUrl(
|
||||
url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
if (!launched && context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Could not open browser.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Error: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.bug_report),
|
||||
|
||||
@@ -57,6 +57,8 @@ dev_dependencies:
|
||||
mockito: ^5.4.4
|
||||
fake_async: ^1.3.1
|
||||
sqlite3: any # used directly in test/unit/db_test_helper.dart
|
||||
url_launcher_platform_interface: ^2.3.2
|
||||
plugin_platform_interface: ^2.1.8
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
import 'package:sharedinbox/ui/screens/crash_screen.dart';
|
||||
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
|
||||
|
||||
class MockUrlLauncher extends Mock
|
||||
with MockPlatformInterfaceMixin
|
||||
implements UrlLauncherPlatform {
|
||||
String? launchedUrl;
|
||||
LaunchOptions? launchOptions;
|
||||
|
||||
@override
|
||||
Future<bool> canLaunch(String? url) async => true;
|
||||
|
||||
@override
|
||||
Future<bool> launchUrl(String? url, LaunchOptions? options) async {
|
||||
launchedUrl = url;
|
||||
launchOptions = options;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('CrashScreen shows error details and has a report button',
|
||||
(tester) async {
|
||||
tester.view.physicalSize = const Size(800, 1200);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(() => tester.view.resetPhysicalSize());
|
||||
|
||||
final mock = MockUrlLauncher();
|
||||
UrlLauncherPlatform.instance = mock;
|
||||
|
||||
const exception = 'TestException: something broke';
|
||||
final stackTrace = StackTrace.current;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: CrashScreen(exception: exception, stackTrace: stackTrace),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.textContaining('TestException'), findsOneWidget);
|
||||
expect(find.text('Report Issue on Codeberg'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Report Issue on Codeberg'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
mock.launchedUrl,
|
||||
contains('https://codeberg.org/guettli/sharedinbox/issues/new'),
|
||||
);
|
||||
expect(
|
||||
mock.launchedUrl,
|
||||
contains('title=Crash%3A%20TestException%3A%20something%20broke'),
|
||||
);
|
||||
expect(
|
||||
mock.launchedUrl,
|
||||
contains('body=Error%3A%20TestException%3A%20something%20broke'),
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user