diff --git a/lib/ui/screens/crash_screen.dart b/lib/ui/screens/crash_screen.dart index 02c49f3..7672ee7 100644 --- a/lib/ui/screens/crash_screen.dart +++ b/lib/ui/screens/crash_screen.dart @@ -25,10 +25,13 @@ class CrashScreen extends StatelessWidget { } catch (_) {} final platform = '${Platform.operatingSystem} ${Platform.operatingSystemVersion}'; + final versionDisplay = gitHash.isNotEmpty + ? '[$version](https://codeberg.org/guettli/sharedinbox/commit/$gitHash)' + : version; final gitLine = gitHash.isNotEmpty ? 'Git Commit: [$gitHash](https://codeberg.org/guettli/sharedinbox/commit/$gitHash)\n' : ''; - return 'App Version: $version\n' + return 'App Version: $versionDisplay\n' '$gitLine' 'Platform: $platform\n\n' 'Error:\n```\n$exception\n```\n\n' @@ -58,6 +61,35 @@ class CrashScreen extends StatelessWidget { ), if (gitHash.isNotEmpty) ...[ const SizedBox(height: 8), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (_, snapshot) { + if (!snapshot.hasData) return const SizedBox.shrink(); + final version = + '${snapshot.data!.version}+${snapshot.data!.buildNumber}'; + return GestureDetector( + onTap: () async { + final url = Uri.parse( + 'https://codeberg.org/guettli/sharedinbox/commit/$gitHash', + ); + await launchUrl( + url, + mode: LaunchMode.externalApplication, + ); + }, + child: Text( + 'App Version: $version', + style: const TextStyle( + fontSize: 12, + color: Colors.blue, + decoration: TextDecoration.underline, + ), + textAlign: TextAlign.center, + ), + ); + }, + ), + const SizedBox(height: 4), GestureDetector( onTap: () async { final url = Uri.parse( diff --git a/test/widget/crash_screen_test.dart b/test/widget/crash_screen_test.dart index 80e5106..ebca2de 100644 --- a/test/widget/crash_screen_test.dart +++ b/test/widget/crash_screen_test.dart @@ -144,6 +144,7 @@ void main() { gitHash: testHash, ), ); + await tester.pumpAndSettle(); // Git hash link should be present final gitLinkFinder = find.textContaining('Git Commit: abc1234'); @@ -167,6 +168,109 @@ void main() { }, ); + testWidgets( + 'CrashScreen shows app version as clickable link when git hash is set', + (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: version link test'; + final stackTrace = StackTrace.current; + const testHash = 'abc1234'; + + await tester.pumpWidget( + CrashScreen( + exception: exception, + stackTrace: stackTrace, + gitHash: testHash, + ), + ); + await tester.pumpAndSettle(); + + // App version link should be present (mocked as 1.0.0+42) + final versionLinkFinder = find.textContaining('App Version: 1.0.0+42'); + expect(versionLinkFinder, findsOneWidget); + + // It must appear above the git hash link + final gitLinkFinder = find.textContaining('Git Commit: abc1234'); + expect( + tester.getTopLeft(versionLinkFinder).dy, + lessThan(tester.getTopLeft(gitLinkFinder).dy), + ); + + // Tapping it should open the Codeberg commit URL + await tester.tap(versionLinkFinder); + await tester.pumpAndSettle(); + + expect( + mock.launchedUrl, + equals('https://codeberg.org/guettli/sharedinbox/commit/abc1234'), + ); + }, + ); + + testWidgets( + 'CrashScreen copy-to-clipboard includes app version as markdown link when git hash is set', + (tester) async { + tester.view.physicalSize = const Size(800, 1200); + tester.view.devicePixelRatio = 1.0; + addTearDown(() => tester.view.resetPhysicalSize()); + + String? clipboardText; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( + SystemChannels.platform, + (MethodCall call) async { + if (call.method == 'Clipboard.setData') { + clipboardText = + (call.arguments as Map)['text'] as String?; + } + return null; + }, + ); + addTearDown( + () => tester.binding.defaultBinaryMessenger + .setMockMethodCallHandler(SystemChannels.platform, null), + ); + + const exception = 'TestException: version link clipboard test'; + final stackTrace = StackTrace.current; + const testHash = 'abc1234'; + + await tester.pumpWidget( + CrashScreen( + exception: exception, + stackTrace: stackTrace, + gitHash: testHash, + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.text('Copy to Clipboard')); + await tester.pump(); + await tester.pump(); + await tester.pumpAndSettle(); + + expect(clipboardText, isNotNull); + // App Version must be a markdown link pointing to the commit + expect( + clipboardText, + contains( + 'App Version: [1.0.0+42](https://codeberg.org/guettli/sharedinbox/commit/abc1234)', + ), + ); + expect( + clipboardText, + contains( + 'Git Commit: [abc1234](https://codeberg.org/guettli/sharedinbox/commit/abc1234)', + ), + ); + }, + ); + testWidgets( 'CrashScreen used as root widget — buttons work without ScaffoldMessenger crash', (tester) async {