Skip to content

Commit df66e0a

Browse files
committed
actions test: Cover PlatformActions.launchUrl
1 parent eb9883f commit df66e0a

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

test/model/binding.dart

+26-1
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,18 @@ class TestZulipBinding extends ZulipBinding {
161161

162162
/// The value that `ZulipBinding.instance.launchUrl()` should return.
163163
///
164-
/// See also [takeLaunchUrlCalls].
164+
/// See also:
165+
/// * [launchUrlException]
166+
/// * [takeLaunchUrlCalls]
165167
bool launchUrlResult = true;
166168

169+
/// The [PlatformException] that `ZulipBinding.instance.launchUrl()` should throw.
170+
///
171+
/// See also:
172+
/// * [launchUrlResult]
173+
/// * [takeLaunchUrlCalls]
174+
PlatformException? launchUrlException;
175+
167176
void _resetLaunchUrl() {
168177
launchUrlResult = true;
169178
_launchUrlCalls = null;
@@ -189,6 +198,22 @@ class TestZulipBinding extends ZulipBinding {
189198
url_launcher.LaunchMode mode = url_launcher.LaunchMode.platformDefault,
190199
}) async {
191200
(_launchUrlCalls ??= []).add((url: url, mode: mode));
201+
202+
if (!launchUrlResult && launchUrlException != null) {
203+
throw FlutterError.fromParts([
204+
ErrorSummary(
205+
'TestZulipBinding.launchUrl called '
206+
'with launchUrlResult: false and non-null launchUrlException'),
207+
ErrorHint(
208+
'Tests should either set launchUrlResult or launchUrlException, '
209+
'but not both.'),
210+
]);
211+
}
212+
213+
if (launchUrlException != null) {
214+
throw launchUrlException!;
215+
}
216+
192217
return launchUrlResult;
193218
}
194219

test/model/settings_test.dart

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ void main() {
1414
final nonHttpLink = Uri.parse('mailto:[email protected]');
1515

1616
group('getUrlLaunchMode', () {
17+
// See also test/widgets/actions_test.dart, where we test that the setting
18+
// is actually used when we open links, with PlatformActions.launchUrl.
19+
1720
testAndroidIos('globalSettings.browserPreference is null; use our per-platform defaults for HTTP links', () {
1821
final globalStore = eg.globalStore(globalSettings: GlobalSettingsData(
1922
browserPreference: null));

test/widgets/actions_test.dart

+92
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:convert';
22

33
import 'package:checks/checks.dart';
4+
import 'package:flutter/foundation.dart';
45
import 'package:flutter/material.dart';
56
import 'package:flutter/services.dart';
67
import 'package:flutter_test/flutter_test.dart';
@@ -12,6 +13,7 @@ import 'package:zulip/api/route/messages.dart';
1213
import 'package:zulip/model/binding.dart';
1314
import 'package:zulip/model/localizations.dart';
1415
import 'package:zulip/model/narrow.dart';
16+
import 'package:zulip/model/settings.dart';
1517
import 'package:zulip/model/store.dart';
1618
import '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('http://chat.zulip.org');
435+
final nonHttpUrl = Uri.parse('mailto:[email protected]');
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\nerror message');
507+
}, variant: androidIosVariant);
508+
});
417509
});
418510
}

0 commit comments

Comments
 (0)