Skip to content

Commit 117d988

Browse files
authored
Add ConnectivityIntegration for web (#1765)
1 parent d8519f9 commit 117d988

9 files changed

+206
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
- Add option to opt out of fatal level for automatically collected errors ([#1738](https://github.com/getsentry/sentry-dart/pull/1738))
1313
- Add `Hive` breadcrumbs ([#1773](https://github.com/getsentry/sentry-dart/pull/1773))
14+
- Add `ConnectivityIntegration` for web ([#1765](https://github.com/getsentry/sentry-dart/pull/1765))
15+
- We only get the info if online/offline on web platform. The added breadcrumb is set to either `wifi` or `none`.
1416

1517
### Dependencies
1618

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:meta/meta.dart';
2+
import '../../../sentry_flutter.dart';
3+
import 'connectivity_provider.dart';
4+
5+
class ConnectivityIntegration extends Integration<SentryFlutterOptions> {
6+
Hub? _hub;
7+
ConnectivityProvider? _connectivityProvider;
8+
9+
@override
10+
void call(Hub hub, SentryFlutterOptions options) {
11+
_hub = hub;
12+
_connectivityProvider = ConnectivityProvider();
13+
_connectivityProvider?.listen((connectivity) {
14+
addBreadcrumb(connectivity);
15+
});
16+
options.sdk.addIntegration('connectivityIntegration');
17+
}
18+
19+
@override
20+
void close() {
21+
_hub = null;
22+
_connectivityProvider?.cancel();
23+
}
24+
25+
@internal
26+
@visibleForTesting
27+
void addBreadcrumb(String connectivity) {
28+
_hub?.addBreadcrumb(
29+
Breadcrumb(
30+
category: 'device.connectivity',
31+
level: SentryLevel.info,
32+
type: 'connectivity',
33+
data: {'connectivity': connectivity}),
34+
);
35+
}
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'noop_connectivity_provider.dart'
2+
if (dart.library.html) 'web_connectivity_provider.dart';
3+
4+
abstract class ConnectivityProvider {
5+
factory ConnectivityProvider() => connectivityProvider();
6+
7+
void listen(void Function(String connectivity) onChange);
8+
void cancel();
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import 'connectivity_provider.dart';
2+
3+
ConnectivityProvider connectivityProvider() {
4+
return NoOpConnectivityProvider();
5+
}
6+
7+
class NoOpConnectivityProvider implements ConnectivityProvider {
8+
@override
9+
void listen(void Function(String connectivity) onChange) {
10+
// NoOp
11+
}
12+
13+
@override
14+
void cancel() {
15+
// NoOp
16+
}
17+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'dart:async';
2+
import 'dart:html' as html;
3+
4+
import 'connectivity_provider.dart';
5+
6+
ConnectivityProvider connectivityProvider() {
7+
return WebConnectivityProvider();
8+
}
9+
10+
class WebConnectivityProvider implements ConnectivityProvider {
11+
StreamSubscription<html.Event>? _onOnlineSub;
12+
StreamSubscription<html.Event>? _onOfflineSub;
13+
14+
@override
15+
void listen(void Function(String connectivity) onChange) {
16+
_onOnlineSub = html.window.onOnline.listen((_) {
17+
onChange('wifi');
18+
});
19+
_onOfflineSub = html.window.onOffline.listen((_) {
20+
onChange('none');
21+
});
22+
}
23+
24+
@override
25+
void cancel() {
26+
_onOnlineSub?.cancel();
27+
_onOnlineSub = null;
28+
29+
_onOfflineSub?.cancel();
30+
_onOfflineSub = null;
31+
}
32+
}

flutter/lib/src/sentry_flutter.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import '../sentry_flutter.dart';
99
import 'event_processor/android_platform_exception_event_processor.dart';
1010
import 'event_processor/flutter_exception_event_processor.dart';
1111
import 'event_processor/platform_exception_event_processor.dart';
12+
import 'integrations/connectivity/connectivity_integration.dart';
1213
import 'integrations/screenshot_integration.dart';
1314
import 'native/factory.dart';
1415
import 'native/native_scope_observer.dart';
@@ -171,6 +172,10 @@ mixin SentryFlutter {
171172
integrations.add(ScreenshotIntegration());
172173
}
173174

175+
if (platformChecker.isWeb) {
176+
integrations.add(ConnectivityIntegration());
177+
}
178+
174179
// works with Skia, CanvasKit and HTML renderer
175180
integrations.add(SentryViewHierarchyIntegration());
176181

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:mockito/mockito.dart';
4+
import 'package:sentry/sentry.dart';
5+
import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart';
6+
import 'package:sentry_flutter/src/sentry_flutter_options.dart';
7+
8+
import '../mocks.dart';
9+
import '../mocks.mocks.dart';
10+
11+
void main() {
12+
WidgetsFlutterBinding.ensureInitialized();
13+
14+
late Fixture fixture;
15+
16+
setUp(() {
17+
fixture = Fixture();
18+
});
19+
20+
verifyBreadcrumb(Breadcrumb crumb, String connectivityData) {
21+
expect(crumb.category, 'device.connectivity');
22+
expect(crumb.type, 'connectivity');
23+
expect(crumb.level, SentryLevel.info);
24+
expect(crumb.data?['connectivity'], connectivityData);
25+
}
26+
27+
test('adds integration', () {
28+
final sut = fixture.getSut();
29+
sut(fixture.hub, fixture.options);
30+
31+
expect(fixture.options.sdk.integrations.contains('connectivityIntegration'),
32+
true);
33+
});
34+
35+
test('$ConnectivityIntegration: addsBreadcrumb', () {
36+
final integration = fixture.getSut();
37+
integration.call(fixture.hub, fixture.options);
38+
39+
integration.addBreadcrumb('wifi');
40+
41+
final crumb = verify(
42+
fixture.hub.addBreadcrumb(captureAny),
43+
).captured.first as Breadcrumb;
44+
45+
verifyBreadcrumb(crumb, 'wifi');
46+
});
47+
}
48+
49+
class Fixture {
50+
final hub = MockHub();
51+
final options = SentryFlutterOptions(dsn: fakeDsn);
52+
53+
ConnectivityIntegration getSut() {
54+
return ConnectivityIntegration();
55+
}
56+
}

flutter/test/sentry_flutter_options_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void main() {
3232
expect(options.enableAutoNativeBreadcrumbs, isFalse);
3333
});
3434

35-
testWidgets('useFlutterBreadcrumbTracking', (WidgetTester tester) async {
35+
testWidgets('useNativeBreadcrumbTracking', (WidgetTester tester) async {
3636
final options = SentryFlutterOptions();
3737
options.useNativeBreadcrumbTracking();
3838

flutter/test/sentry_flutter_test.dart

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import 'package:flutter_test/flutter_test.dart';
44
import 'package:package_info_plus/package_info_plus.dart';
55
import 'package:sentry_flutter/sentry_flutter.dart';
6+
import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart';
67
import 'package:sentry_flutter/src/integrations/integrations.dart';
78
import 'package:sentry_flutter/src/integrations/screenshot_integration.dart';
89
import 'package:sentry_flutter/src/profiling.dart';
@@ -23,6 +24,10 @@ final platformAgnosticIntegrations = [
2324
SentryViewHierarchyIntegration,
2425
];
2526

27+
final webIntegrations = [
28+
ConnectivityIntegration,
29+
];
30+
2631
final nonWebIntegrations = [
2732
OnErrorIntegration,
2833
];
@@ -81,14 +86,18 @@ void main() {
8186
options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true);
8287

8388
testConfiguration(
84-
integrations: integrations,
85-
shouldHaveIntegrations: [
86-
...androidIntegrations,
87-
...nativeIntegrations,
88-
...platformAgnosticIntegrations,
89-
...nonWebIntegrations,
90-
],
91-
shouldNotHaveIntegrations: iOsAndMacOsIntegrations);
89+
integrations: integrations,
90+
shouldHaveIntegrations: [
91+
...androidIntegrations,
92+
...nativeIntegrations,
93+
...platformAgnosticIntegrations,
94+
...nonWebIntegrations,
95+
],
96+
shouldNotHaveIntegrations: [
97+
...iOsAndMacOsIntegrations,
98+
...nonWebIntegrations,
99+
],
100+
);
92101

93102
integrations
94103
.indexWhere((element) => element is WidgetsFlutterBindingIntegration);
@@ -138,7 +147,10 @@ void main() {
138147
...platformAgnosticIntegrations,
139148
...nonWebIntegrations,
140149
],
141-
shouldNotHaveIntegrations: androidIntegrations,
150+
shouldNotHaveIntegrations: [
151+
...androidIntegrations,
152+
...nonWebIntegrations,
153+
],
142154
);
143155

144156
testBefore(
@@ -179,16 +191,15 @@ void main() {
179191
testScopeObserver(
180192
options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true);
181193

182-
testConfiguration(
183-
integrations: integrations,
184-
shouldHaveIntegrations: [
185-
...iOsAndMacOsIntegrations,
186-
...nativeIntegrations,
187-
...platformAgnosticIntegrations,
188-
...nonWebIntegrations,
189-
],
190-
shouldNotHaveIntegrations: androidIntegrations,
191-
);
194+
testConfiguration(integrations: integrations, shouldHaveIntegrations: [
195+
...iOsAndMacOsIntegrations,
196+
...nativeIntegrations,
197+
...platformAgnosticIntegrations,
198+
...nonWebIntegrations,
199+
], shouldNotHaveIntegrations: [
200+
...androidIntegrations,
201+
...nonWebIntegrations,
202+
]);
192203

193204
testBefore(
194205
integrations: integrations,
@@ -239,6 +250,7 @@ void main() {
239250
...androidIntegrations,
240251
...iOsAndMacOsIntegrations,
241252
...nativeIntegrations,
253+
...webIntegrations,
242254
],
243255
);
244256

@@ -290,6 +302,7 @@ void main() {
290302
...androidIntegrations,
291303
...iOsAndMacOsIntegrations,
292304
...nativeIntegrations,
305+
...webIntegrations,
293306
],
294307
);
295308

@@ -336,7 +349,10 @@ void main() {
336349

337350
testConfiguration(
338351
integrations: integrations,
339-
shouldHaveIntegrations: platformAgnosticIntegrations,
352+
shouldHaveIntegrations: [
353+
...platformAgnosticIntegrations,
354+
...webIntegrations,
355+
],
340356
shouldNotHaveIntegrations: [
341357
...androidIntegrations,
342358
...iOsAndMacOsIntegrations,
@@ -383,7 +399,10 @@ void main() {
383399

384400
testConfiguration(
385401
integrations: integrations,
386-
shouldHaveIntegrations: platformAgnosticIntegrations,
402+
shouldHaveIntegrations: [
403+
...platformAgnosticIntegrations,
404+
...webIntegrations,
405+
],
387406
shouldNotHaveIntegrations: [
388407
...androidIntegrations,
389408
...iOsAndMacOsIntegrations,
@@ -427,7 +446,10 @@ void main() {
427446

428447
testConfiguration(
429448
integrations: integrations,
430-
shouldHaveIntegrations: platformAgnosticIntegrations,
449+
shouldHaveIntegrations: [
450+
...platformAgnosticIntegrations,
451+
...webIntegrations,
452+
],
431453
shouldNotHaveIntegrations: [
432454
...androidIntegrations,
433455
...iOsAndMacOsIntegrations,
@@ -472,7 +494,10 @@ void main() {
472494

473495
testConfiguration(
474496
integrations: integrations,
475-
shouldHaveIntegrations: platformAgnosticIntegrations,
497+
shouldHaveIntegrations: [
498+
...platformAgnosticIntegrations,
499+
...webIntegrations,
500+
],
476501
shouldNotHaveIntegrations: [
477502
...androidIntegrations,
478503
...iOsAndMacOsIntegrations,

0 commit comments

Comments
 (0)