Skip to content

Commit 491a249

Browse files
committed
binding: Wrap url_launcher's launchUrl
1 parent db44a9b commit 491a249

File tree

2 files changed

+74
-19
lines changed

2 files changed

+74
-19
lines changed

lib/model/binding.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'package:flutter/foundation.dart';
2+
import 'package:url_launcher/url_launcher.dart' as url_launcher;
3+
export 'package:url_launcher/url_launcher.dart' show LaunchMode;
24

35
import '../widgets/store.dart';
46
import 'store.dart';
57

6-
/// A singleton service providing the app's data.
8+
/// A singleton service providing the app's data and use of Flutter plugins.
79
///
810
/// Only one instance will be constructed in the lifetime of the app,
911
/// by calling the `ensureInitialized` static method on a subclass.
@@ -62,13 +64,24 @@ abstract class ZulipBinding {
6264
/// In application code, use [GlobalStoreWidget.of] to get access
6365
/// to a [GlobalStore].
6466
Future<GlobalStore> loadGlobalStore();
67+
68+
/// Pass [url] to the underlying platform, via package:url_launcher.
69+
///
70+
/// This wraps [url_launcher.launchUrl].
71+
Future<bool> launchUrl(
72+
Uri url, {
73+
url_launcher.LaunchMode mode = url_launcher.LaunchMode.platformDefault,
74+
});
6575
}
6676

6777
/// A concrete binding for use in the live application.
6878
///
6979
/// The global store returned by [loadGlobalStore], and consequently by
7080
/// [GlobalStoreWidget.of] in application code, will be a [LiveGlobalStore].
7181
/// It therefore uses a live server and live, persistent local database.
82+
///
83+
/// Methods wrapping a plugin, like [launchUrl], invoke the actual
84+
/// underlying plugin method.
7285
class LiveZulipBinding extends ZulipBinding {
7386
/// Initialize the binding if necessary, and ensure it is a [LiveZulipBinding].
7487
static LiveZulipBinding ensureInitialized() {
@@ -82,4 +95,12 @@ class LiveZulipBinding extends ZulipBinding {
8295
Future<GlobalStore> loadGlobalStore() {
8396
return LiveGlobalStore.load();
8497
}
98+
99+
@override
100+
Future<bool> launchUrl(
101+
Uri url, {
102+
url_launcher.LaunchMode mode = url_launcher.LaunchMode.platformDefault,
103+
}) {
104+
return url_launcher.launchUrl(url, mode: mode);
105+
}
85106
}

test/model/binding.dart

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import 'package:flutter/foundation.dart';
2+
import 'package:url_launcher/url_launcher.dart' as url_launcher;
23
import 'package:zulip/model/binding.dart';
34
import 'package:zulip/model/store.dart';
45

56
import 'test_store.dart';
67

78
/// A concrete binding for use in the `flutter test` environment.
89
///
9-
/// Tests that will mount a [GlobalStoreWidget] should initialize this binding
10+
/// Tests that will mount a [GlobalStoreWidget], or invoke a Flutter plugin,
11+
/// should initialize this binding
1012
/// by calling [ensureInitialized] at the start of the `main` method.
1113
///
1214
/// Individual test functions that mount a [GlobalStoreWidget] may then use
@@ -41,37 +43,41 @@ class TestZulipBinding extends ZulipBinding {
4143
_instance = this;
4244
}
4345

44-
/// The current global store offered to a [GlobalStoreWidget].
45-
///
46-
/// The store is created lazily when accessing this getter, or when mounting
47-
/// a [GlobalStoreWidget]. The same store will continue to be provided until
48-
/// a call to [reset].
49-
///
50-
/// Tests that access this getter, or that mount a [GlobalStoreWidget],
51-
/// should clean up by calling [reset].
52-
TestGlobalStore get globalStore => _globalStore ??= TestGlobalStore(accounts: []);
53-
TestGlobalStore? _globalStore;
54-
55-
bool _debugAlreadyLoaded = false;
56-
5746
/// Reset all test data to a clean state.
5847
///
59-
/// Tests that mount a [GlobalStoreWidget], or that access [globalStore],
48+
/// Tests that mount a [GlobalStoreWidget], or invoke a Flutter plugin,
49+
/// or access [globalStore] or other methods on this class,
6050
/// should clean up by calling this method. Typically this is done using
6151
/// [addTearDown], like `addTearDown(TestZulipBinding.instance.reset);`.
6252
void reset() {
6353
_globalStore?.dispose();
6454
_globalStore = null;
6555
assert(() {
66-
_debugAlreadyLoaded = false;
56+
_debugAlreadyLoadedStore = false;
6757
return true;
6858
}());
59+
60+
launchUrlResult = true;
61+
_launchUrlCalls = null;
6962
}
7063

64+
/// The current global store offered to a [GlobalStoreWidget].
65+
///
66+
/// The store is created lazily when accessing this getter, or when mounting
67+
/// a [GlobalStoreWidget]. The same store will continue to be provided until
68+
/// a call to [reset].
69+
///
70+
/// Tests that access this getter, or that mount a [GlobalStoreWidget],
71+
/// should clean up by calling [reset].
72+
TestGlobalStore get globalStore => _globalStore ??= TestGlobalStore(accounts: []);
73+
TestGlobalStore? _globalStore;
74+
75+
bool _debugAlreadyLoadedStore = false;
76+
7177
@override
7278
Future<GlobalStore> loadGlobalStore() {
7379
assert(() {
74-
if (_debugAlreadyLoaded) {
80+
if (_debugAlreadyLoadedStore) {
7581
throw FlutterError.fromParts([
7682
ErrorSummary('The same test global store was loaded twice.'),
7783
ErrorDescription(
@@ -87,9 +93,37 @@ class TestZulipBinding extends ZulipBinding {
8793
),
8894
]);
8995
}
90-
_debugAlreadyLoaded = true;
96+
_debugAlreadyLoadedStore = true;
9197
return true;
9298
}());
9399
return Future.value(globalStore);
94100
}
101+
102+
/// The value that `ZulipBinding.instance.launchUrl()` should return.
103+
///
104+
/// See also [takeLaunchUrlCalls].
105+
bool launchUrlResult = true;
106+
107+
/// Consume the log of calls made to `ZulipBinding.instance.launchUrl()`.
108+
///
109+
/// This returns a list of the arguments to all calls made
110+
/// to `ZulipBinding.instance.launchUrl()` since the last call to
111+
/// either this method or [reset].
112+
///
113+
/// See also [launchUrlResult].
114+
List<({Uri url, url_launcher.LaunchMode mode})> takeLaunchUrlCalls() {
115+
final result = _launchUrlCalls;
116+
_launchUrlCalls = null;
117+
return result ?? [];
118+
}
119+
List<({Uri url, url_launcher.LaunchMode mode})>? _launchUrlCalls;
120+
121+
@override
122+
Future<bool> launchUrl(
123+
Uri url, {
124+
url_launcher.LaunchMode mode = url_launcher.LaunchMode.platformDefault,
125+
}) async {
126+
(_launchUrlCalls ??= []).add((url: url, mode: mode));
127+
return launchUrlResult;
128+
}
95129
}

0 commit comments

Comments
 (0)