Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ConnectivityIntegration for web #1765

Merged
merged 11 commits into from
Dec 18, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

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

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:meta/meta.dart';
import '../../../sentry_flutter.dart';
import 'connectivity_provider.dart';

class ConnectivityIntegration extends Integration<SentryFlutterOptions> {
Hub? _hub;
ConnectivityProvider? _connectivityProvider;

@override
void call(Hub hub, SentryFlutterOptions options) {
_hub = hub;
_connectivityProvider = ConnectivityProvider();
_connectivityProvider?.listen((connectivity) {
addBreadcrumb(connectivity);
});
options.sdk.addIntegration('connectivityIntegration');
}

@override
void close() {
_hub = null;
_connectivityProvider?.cancel();
}

@internal
@visibleForTesting
void addBreadcrumb(String connectivity) {
_hub?.addBreadcrumb(
Breadcrumb(
category: 'device.connectivity',
level: SentryLevel.info,
type: 'connectivity',
data: {'connectivity': connectivity}),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'noop_connectivity_provider.dart'
if (dart.library.html) 'web_connectivity_provider.dart';

abstract class ConnectivityProvider {
factory ConnectivityProvider() => connectivityProvider();

void listen(void Function(String connectivity) onChange);
void cancel();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'connectivity_provider.dart';

ConnectivityProvider connectivityProvider() {
return NoOpConnectivityProvider();
}

class NoOpConnectivityProvider implements ConnectivityProvider {
@override
void listen(void Function(String connectivity) onChange) {
// NoOp
}

@override
void cancel() {
// NoOp
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'dart:async';
import 'dart:html' as html;

import 'connectivity_provider.dart';

ConnectivityProvider connectivityProvider() {
return WebConnectivityProvider();
}

class WebConnectivityProvider implements ConnectivityProvider {
StreamSubscription<html.Event>? _onOnlineSub;
StreamSubscription<html.Event>? _onOfflineSub;

@override
void listen(void Function(String connectivity) onChange) {
_onOnlineSub = html.window.onOnline.listen((_) {
onChange('wifi');
});
_onOfflineSub = html.window.onOffline.listen((_) {
onChange('none');
});
}

@override
void cancel() {
_onOnlineSub?.cancel();
_onOnlineSub = null;

_onOfflineSub?.cancel();
_onOfflineSub = null;
}
}
5 changes: 5 additions & 0 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import '../sentry_flutter.dart';
import 'event_processor/android_platform_exception_event_processor.dart';
import 'event_processor/flutter_exception_event_processor.dart';
import 'event_processor/platform_exception_event_processor.dart';
import 'integrations/connectivity/connectivity_integration.dart';
import 'integrations/screenshot_integration.dart';
import 'native/factory.dart';
import 'native/native_scope_observer.dart';
Expand Down Expand Up @@ -171,6 +172,10 @@ mixin SentryFlutter {
integrations.add(ScreenshotIntegration());
}

if (platformChecker.isWeb) {
integrations.add(ConnectivityIntegration());
}

// works with Skia, CanvasKit and HTML renderer
integrations.add(SentryViewHierarchyIntegration());

Expand Down
56 changes: 56 additions & 0 deletions flutter/test/integrations/connectivity_integration_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart';
import 'package:sentry_flutter/src/sentry_flutter_options.dart';

import '../mocks.dart';
import '../mocks.mocks.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();

late Fixture fixture;

setUp(() {
fixture = Fixture();
});

verifyBreadcrumb(Breadcrumb crumb, String connectivityData) {
expect(crumb.category, 'device.connectivity');
expect(crumb.type, 'connectivity');
expect(crumb.level, SentryLevel.info);
expect(crumb.data?['connectivity'], connectivityData);
}

test('adds integration', () {
final sut = fixture.getSut();
sut(fixture.hub, fixture.options);

expect(fixture.options.sdk.integrations.contains('connectivityIntegration'),
true);
});

test('$ConnectivityIntegration: addsBreadcrumb', () {
final integration = fixture.getSut();
integration.call(fixture.hub, fixture.options);

integration.addBreadcrumb('wifi');

final crumb = verify(
fixture.hub.addBreadcrumb(captureAny),
).captured.first as Breadcrumb;

verifyBreadcrumb(crumb, 'wifi');
});
}

class Fixture {
final hub = MockHub();
final options = SentryFlutterOptions(dsn: fakeDsn);

ConnectivityIntegration getSut() {
return ConnectivityIntegration();
}
}
2 changes: 1 addition & 1 deletion flutter/test/sentry_flutter_options_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void main() {
expect(options.enableAutoNativeBreadcrumbs, isFalse);
});

testWidgets('useFlutterBreadcrumbTracking', (WidgetTester tester) async {
testWidgets('useNativeBreadcrumbTracking', (WidgetTester tester) async {
final options = SentryFlutterOptions();
options.useNativeBreadcrumbTracking();

Expand Down
71 changes: 48 additions & 23 deletions flutter/test/sentry_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter/src/integrations/connectivity/connectivity_integration.dart';
import 'package:sentry_flutter/src/integrations/integrations.dart';
import 'package:sentry_flutter/src/integrations/screenshot_integration.dart';
import 'package:sentry_flutter/src/profiling.dart';
Expand All @@ -23,6 +24,10 @@ final platformAgnosticIntegrations = [
SentryViewHierarchyIntegration,
];

final webIntegrations = [
ConnectivityIntegration,
];

final nonWebIntegrations = [
OnErrorIntegration,
];
Expand Down Expand Up @@ -81,14 +86,18 @@ void main() {
options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true);

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: [
...androidIntegrations,
...nativeIntegrations,
...platformAgnosticIntegrations,
...nonWebIntegrations,
],
shouldNotHaveIntegrations: iOsAndMacOsIntegrations);
integrations: integrations,
shouldHaveIntegrations: [
...androidIntegrations,
...nativeIntegrations,
...platformAgnosticIntegrations,
...nonWebIntegrations,
],
shouldNotHaveIntegrations: [
...iOsAndMacOsIntegrations,
...nonWebIntegrations,
],
);

integrations
.indexWhere((element) => element is WidgetsFlutterBindingIntegration);
Expand Down Expand Up @@ -138,7 +147,10 @@ void main() {
...platformAgnosticIntegrations,
...nonWebIntegrations,
],
shouldNotHaveIntegrations: androidIntegrations,
shouldNotHaveIntegrations: [
...androidIntegrations,
...nonWebIntegrations,
],
);

testBefore(
Expand Down Expand Up @@ -179,16 +191,15 @@ void main() {
testScopeObserver(
options: sentryFlutterOptions!, expectedHasNativeScopeObserver: true);

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: [
...iOsAndMacOsIntegrations,
...nativeIntegrations,
...platformAgnosticIntegrations,
...nonWebIntegrations,
],
shouldNotHaveIntegrations: androidIntegrations,
);
testConfiguration(integrations: integrations, shouldHaveIntegrations: [
...iOsAndMacOsIntegrations,
...nativeIntegrations,
...platformAgnosticIntegrations,
...nonWebIntegrations,
], shouldNotHaveIntegrations: [
...androidIntegrations,
...nonWebIntegrations,
]);

testBefore(
integrations: integrations,
Expand Down Expand Up @@ -239,6 +250,7 @@ void main() {
...androidIntegrations,
...iOsAndMacOsIntegrations,
...nativeIntegrations,
...webIntegrations,
],
);

Expand Down Expand Up @@ -290,6 +302,7 @@ void main() {
...androidIntegrations,
...iOsAndMacOsIntegrations,
...nativeIntegrations,
...webIntegrations,
],
);

Expand Down Expand Up @@ -336,7 +349,10 @@ void main() {

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: platformAgnosticIntegrations,
shouldHaveIntegrations: [
...platformAgnosticIntegrations,
...webIntegrations,
],
shouldNotHaveIntegrations: [
...androidIntegrations,
...iOsAndMacOsIntegrations,
Expand Down Expand Up @@ -383,7 +399,10 @@ void main() {

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: platformAgnosticIntegrations,
shouldHaveIntegrations: [
...platformAgnosticIntegrations,
...webIntegrations,
],
shouldNotHaveIntegrations: [
...androidIntegrations,
...iOsAndMacOsIntegrations,
Expand Down Expand Up @@ -427,7 +446,10 @@ void main() {

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: platformAgnosticIntegrations,
shouldHaveIntegrations: [
...platformAgnosticIntegrations,
...webIntegrations,
],
shouldNotHaveIntegrations: [
...androidIntegrations,
...iOsAndMacOsIntegrations,
Expand Down Expand Up @@ -472,7 +494,10 @@ void main() {

testConfiguration(
integrations: integrations,
shouldHaveIntegrations: platformAgnosticIntegrations,
shouldHaveIntegrations: [
...platformAgnosticIntegrations,
...webIntegrations,
],
shouldNotHaveIntegrations: [
...androidIntegrations,
...iOsAndMacOsIntegrations,
Expand Down