11import 'dart:convert' ;
22
33import 'package:checks/checks.dart' ;
4+ import 'package:flutter/foundation.dart' ;
45import 'package:flutter/material.dart' ;
56import 'package:flutter/services.dart' ;
67import 'package:flutter_test/flutter_test.dart' ;
@@ -12,6 +13,7 @@ import 'package:zulip/api/route/messages.dart';
1213import 'package:zulip/model/binding.dart' ;
1314import 'package:zulip/model/localizations.dart' ;
1415import 'package:zulip/model/narrow.dart' ;
16+ import 'package:zulip/model/settings.dart' ;
1517import 'package:zulip/model/store.dart' ;
1618import 'package:zulip/widgets/actions.dart' ;
1719
@@ -414,5 +416,95 @@ void main() {
414416 await checkSnackBar (tester, expected: true );
415417 });
416418 });
419+
420+ group ('launchUrl' , () {
421+ Future <void > call (WidgetTester tester, {required Uri url}) async {
422+ await tester.pumpWidget (TestZulipApp (
423+ child: Builder (builder: (context) => Center (
424+ child: ElevatedButton (
425+ onPressed: () async {
426+ await PlatformActions .launchUrl (context, url);
427+ },
428+ child: const Text ('link' ))))));
429+ await tester.pump ();
430+ await tester.tap (find.text ('link' ));
431+ await tester.pump (Duration .zero);
432+ }
433+
434+ final httpUrl = Uri .parse ('https://chat.example' );
435+ final nonHttpUrl = Uri .parse ('mailto:chat@example' );
436+
437+ Future <void > runAndCheckSuccess (WidgetTester tester, {
438+ required Uri url,
439+ required UrlLaunchMode expectedModeAndroid,
440+ required UrlLaunchMode expectedModeIos,
441+ }) async {
442+ await call (tester, url: url);
443+
444+ final expectedMode = switch (defaultTargetPlatform) {
445+ TargetPlatform .android => expectedModeAndroid,
446+ TargetPlatform .iOS => expectedModeIos,
447+ _ => throw StateError ('attempted to test with $defaultTargetPlatform ' ),
448+ };
449+ check (testBinding.takeLaunchUrlCalls ()).single
450+ .equals ((url: url, mode: expectedMode));
451+ }
452+
453+ final androidIosVariant = TargetPlatformVariant ({TargetPlatform .iOS, TargetPlatform .android});
454+
455+ testWidgets ('globalSettings.browserPreference is null; use our per-platform defaults for HTTP links' , (tester) async {
456+ await testBinding.globalStore.settings.setBrowserPreference (null );
457+ await runAndCheckSuccess (tester,
458+ url: httpUrl,
459+ expectedModeAndroid: UrlLaunchMode .inAppBrowserView,
460+ expectedModeIos: UrlLaunchMode .externalApplication);
461+ }, variant: androidIosVariant);
462+
463+ testWidgets ('globalSettings.browserPreference is null; use our per-platform defaults for non-HTTP links' , (tester) async {
464+ await testBinding.globalStore.settings.setBrowserPreference (null );
465+ await runAndCheckSuccess (tester,
466+ url: nonHttpUrl,
467+ expectedModeAndroid: UrlLaunchMode .platformDefault,
468+ expectedModeIos: UrlLaunchMode .externalApplication);
469+ }, variant: androidIosVariant);
470+
471+ testWidgets ('globalSettings.browserPreference is inApp; follow the user preference for http links' , (tester) async {
472+ await testBinding.globalStore.settings.setBrowserPreference (BrowserPreference .inApp);
473+ await runAndCheckSuccess (tester,
474+ url: httpUrl,
475+ expectedModeAndroid: UrlLaunchMode .inAppBrowserView,
476+ expectedModeIos: UrlLaunchMode .inAppBrowserView);
477+ }, variant: androidIosVariant);
478+
479+ testWidgets ('globalSettings.browserPreference is inApp; use platform default for non-http links' , (tester) async {
480+ await testBinding.globalStore.settings.setBrowserPreference (BrowserPreference .inApp);
481+ await runAndCheckSuccess (tester,
482+ url: nonHttpUrl,
483+ expectedModeAndroid: UrlLaunchMode .platformDefault,
484+ expectedModeIos: UrlLaunchMode .platformDefault);
485+ }, variant: androidIosVariant);
486+
487+ testWidgets ('globalSettings.browserPreference is external; follow the user preference' , (tester) async {
488+ await testBinding.globalStore.settings.setBrowserPreference (BrowserPreference .external );
489+ await runAndCheckSuccess (tester,
490+ url: httpUrl,
491+ expectedModeAndroid: UrlLaunchMode .externalApplication,
492+ expectedModeIos: UrlLaunchMode .externalApplication);
493+ }, variant: androidIosVariant);
494+
495+ testWidgets ('ZulipBinding.launchUrl returns false' , (tester) async {
496+ testBinding.launchUrlResult = false ;
497+ await call (tester, url: httpUrl);
498+ checkErrorDialog (tester, expectedTitle: 'Unable to open link' );
499+ }, variant: androidIosVariant);
500+
501+ testWidgets ('ZulipBinding.launchUrl throws PlatformException' , (tester) async {
502+ testBinding.launchUrlException = PlatformException (code: 'code' , message: 'error message' );
503+ await call (tester, url: httpUrl);
504+ checkErrorDialog (tester,
505+ expectedTitle: 'Unable to open link' ,
506+ expectedMessage: 'Link could not be opened: ${httpUrl .toString ()}\n\n error message' );
507+ }, variant: androidIosVariant);
508+ });
417509 });
418510}
0 commit comments