Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[connectivity_for_web] Migration to null-safety. #3652

Merged
merged 7 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/connectivity/connectivity_for_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.4.0

* Migrate to null-safety
* Run tests using flutter driver

## 0.3.1+4

* Remove unused `test` dependency.
Expand Down
21 changes: 21 additions & 0 deletions packages/connectivity/connectivity_for_web/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Testing

This package utilizes the `integration_test` package to run its tests in a web browser.

See [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info.

## Running the tests

Make sure you have updated to the latest Flutter master.

1. Check what version of Chrome is running on the machine you're running tests on.

2. Download and install driver for that version from here:
* <https://chromedriver.chromium.org/downloads>

3. Start the driver using `chromedriver --port=4444`

4. Run tests: `flutter drive -d web-server --browser-name=chrome --driver=test_driver/integration_driver.dart --target=integration_test/TEST_NAME.dart`, or (in Linux):

* Single: `./run_test.sh integration_test/TEST_NAME.dart`
* All: `./run_test.sh`
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'package:integration_test/integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
import 'package:connectivity_for_web/src/network_information_api_connectivity_plugin.dart';

import 'package:mockito/mockito.dart';
import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'src/connectivity_mocks.dart';

Expand All @@ -12,66 +10,70 @@ void main() {

group('checkConnectivity', () {
void testCheckConnectivity({
String type,
String effectiveType,
num downlink = 10,
num rtt = 50,
ConnectivityResult expected,
String? type,
String? effectiveType,
num? downlink = 10,
int? rtt = 50,
required ConnectivityResult expected,
}) {
final connection = MockNetworkInformation();
when(connection.type).thenReturn(type);
when(connection.effectiveType).thenReturn(effectiveType);
when(connection.downlink).thenReturn(downlink);
when(connection.rtt).thenReturn(downlink);
final connection = FakeNetworkInformation(
type: type,
effectiveType: effectiveType,
downlink: downlink,
rtt: rtt,
);

NetworkInformationApiConnectivityPlugin plugin =
NetworkInformationApiConnectivityPlugin.withConnection(connection);
expect(plugin.checkConnectivity(), completion(equals(expected)));
}

test('0 downlink and rtt -> none', () {
testWidgets('0 downlink and rtt -> none', (WidgetTester tester) async {
testCheckConnectivity(
effectiveType: '4g',
downlink: 0,
rtt: 0,
expected: ConnectivityResult.none);
});
test('slow-2g -> mobile', () {
testWidgets('slow-2g -> mobile', (WidgetTester tester) async {
testCheckConnectivity(
effectiveType: 'slow-2g', expected: ConnectivityResult.mobile);
});
test('2g -> mobile', () {
testWidgets('2g -> mobile', (WidgetTester tester) async {
testCheckConnectivity(
effectiveType: '2g', expected: ConnectivityResult.mobile);
});
test('3g -> mobile', () {
testWidgets('3g -> mobile', (WidgetTester tester) async {
testCheckConnectivity(
effectiveType: '3g', expected: ConnectivityResult.mobile);
});
test('4g -> wifi', () {
testWidgets('4g -> wifi', (WidgetTester tester) async {
testCheckConnectivity(
effectiveType: '4g', expected: ConnectivityResult.wifi);
});
});

group('get onConnectivityChanged', () {
test('puts change events in a Stream', () async {
final connection = MockNetworkInformation();
testWidgets('puts change events in a Stream', (WidgetTester tester) async {
final connection = FakeNetworkInformation();
NetworkInformationApiConnectivityPlugin plugin =
NetworkInformationApiConnectivityPlugin.withConnection(connection);

Stream<ConnectivityResult> results = plugin.onConnectivityChanged;
// The onConnectivityChanged stream is infinite, so we only .take(2) so the test completes.
// We need to do .toList() now, because otherwise the Stream won't be actually listened to,
// and we'll miss the calls to mockChangeValue below.
final results = plugin.onConnectivityChanged.take(2).toList();

// Fake a disconnect-reconnect
await connection.mockChangeValue(downlink: 0, rtt: 0);
await connection.mockChangeValue(
downlink: 10, rtt: 50, effectiveType: '4g');

// The stream of results is infinite, so we need to .take(2) for this test to complete.
// Expect to see the disconnect-reconnect in the resulting stream.
expect(
results.take(2).toList(),
completion(
equals([ConnectivityResult.none, ConnectivityResult.wifi])));
results,
completion([ConnectivityResult.none, ConnectivityResult.wifi]),
);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'dart:async';
import 'dart:html';
import 'dart:js_util' show getProperty;

import 'package:flutter_test/flutter_test.dart';

/// A Fake implementation of the NetworkInformation API that allows
/// for external modification of its values.
///
/// Note that the DOM API works by internally mutating and broadcasting
/// 'change' events.
class FakeNetworkInformation extends Fake implements NetworkInformation {
String? _type;
String? _effectiveType;
num? _downlink;
int? _rtt;

@override
String? get type => _type;

@override
String? get effectiveType => _effectiveType;

@override
num? get downlink => _downlink;

@override
int? get rtt => _rtt;

FakeNetworkInformation({
String? type,
String? effectiveType,
num? downlink,
int? rtt,
}) : this._type = type,
this._effectiveType = effectiveType,
this._downlink = downlink,
this._rtt = rtt;

/// Changes the desired values, and triggers the change event listener.
Future<void> mockChangeValue({
String? type,
String? effectiveType,
num? downlink,
int? rtt,
}) async {
this._type = type;
this._effectiveType = effectiveType;
this._downlink = downlink;
this._rtt = rtt;

// This is set by the onConnectivityChanged getter...
final Function onchange = getProperty(this, 'onchange') as Function;
onchange(Event('change'));
}
}
25 changes: 25 additions & 0 deletions packages/connectivity/connectivity_for_web/example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

/// App for testing
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: Text('Testing... Look at the console output for results!'),
);
}
}
21 changes: 21 additions & 0 deletions packages/connectivity/connectivity_for_web/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: connectivity_for_web_integration_tests
publish_to: none

dependencies:
connectivity_for_web:
path: ../
flutter:
sdk: flutter

dev_dependencies:
js: ^0.6.3
flutter_test:
sdk: flutter
flutter_driver:
sdk: flutter
integration_test:
sdk: flutter

environment:
sdk: ">=2.12.0-259.9.beta <3.0.0"
flutter: ">=1.27.0-0" # For integration_test from sdk
18 changes: 18 additions & 0 deletions packages/connectivity/connectivity_for_web/example/run_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/bash
if pgrep -lf chromedriver > /dev/null; then
echo "chromedriver is running."

if [ $# -eq 0 ]; then
echo "No target specified, running all tests..."
find integration_test/ -iname *_test.dart | xargs -n1 -i -t flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_driver.dart --target='{}'
else
echo "Running test target: $1..."
set -x
flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_driver.dart --target=$1
fi

else
echo "chromedriver is not running."
echo "Please, check the README.md for instructions on how to use run_test.sh"
fi

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() async => integrationDriver();
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@ class DartHtmlConnectivityPlugin extends ConnectivityPlugin {
/// Checks the connection status of the device.
@override
Future<ConnectivityResult> checkConnectivity() async {
return html.window.navigator.onLine
return html.window.navigator.onLine ?? false
? ConnectivityResult.wifi
: ConnectivityResult.none;
}

StreamController<ConnectivityResult> _connectivityResult;
StreamController<ConnectivityResult>? _connectivityResult;

/// Returns a Stream of ConnectivityResults changes.
@override
Stream<ConnectivityResult> get onConnectivityChanged {
if (_connectivityResult == null) {
_connectivityResult = StreamController<ConnectivityResult>();
_connectivityResult = StreamController<ConnectivityResult>.broadcast();
// Fallback to dart:html window.onOnline / window.onOffline
html.window.onOnline.listen((event) {
_connectivityResult.add(ConnectivityResult.wifi);
_connectivityResult!.add(ConnectivityResult.wifi);
});
html.window.onOffline.listen((event) {
_connectivityResult.add(ConnectivityResult.none);
_connectivityResult!.add(ConnectivityResult.none);
});
}
return _connectivityResult.stream;
return _connectivityResult!.stream;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:async';
import 'dart:html' as html show window, NetworkInformation;
import 'dart:js';
import 'dart:js_util';
import 'dart:js' show allowInterop;
import 'dart:js_util' show setProperty;

import 'package:connectivity_platform_interface/connectivity_platform_interface.dart';
import 'package:connectivity_for_web/connectivity_for_web.dart';
Expand All @@ -18,7 +18,7 @@ class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin {

/// The constructor of the plugin.
NetworkInformationApiConnectivityPlugin()
: this.withConnection(html.window.navigator.connection);
: this.withConnection(html.window.navigator.connection!);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ensured by the isSupported method that is used before instantiating this plugin class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems worth documenting that requirement in the constructor comment.


/// Creates the plugin, with an override of the NetworkInformation object.
@visibleForTesting
Expand All @@ -32,17 +32,19 @@ class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin {
return networkInformationToConnectivityResult(_networkInformation);
}

StreamController<ConnectivityResult> _connectivityResultStreamController;
Stream<ConnectivityResult> _connectivityResultStream;
StreamController<ConnectivityResult>? _connectivityResultStreamController;
late Stream<ConnectivityResult> _connectivityResultStream;

/// Returns a Stream of ConnectivityResults changes.
@override
Stream<ConnectivityResult> get onConnectivityChanged {
if (_connectivityResultStreamController == null) {
_connectivityResultStreamController =
StreamController<ConnectivityResult>();

// Directly write the 'onchange' function on the networkInformation object.
setProperty(_networkInformation, 'onchange', allowInterop((_) {
_connectivityResultStreamController
_connectivityResultStreamController!
.add(networkInformationToConnectivityResult(_networkInformation));
}));
// TODO: Implement the above with _networkInformation.onChange:
Expand All @@ -54,7 +56,7 @@ class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin {
// onChange Stream upon hot restart.
// https://github.com/dart-lang/sdk/issues/42679
_connectivityResultStream =
_connectivityResultStreamController.stream.asBroadcastStream();
_connectivityResultStreamController!.stream.asBroadcastStream();
}
return _connectivityResultStream;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:connectivity_platform_interface/connectivity_platform_interface.

/// Converts an incoming NetworkInformation object into the correct ConnectivityResult.
ConnectivityResult networkInformationToConnectivityResult(
html.NetworkInformation info,
html.NetworkInformation? info,
) {
if (info == null) {
return ConnectivityResult.none;
Expand All @@ -12,10 +12,10 @@ ConnectivityResult networkInformationToConnectivityResult(
return ConnectivityResult.none;
}
if (info.effectiveType != null) {
return _effectiveTypeToConnectivityResult(info.effectiveType);
return _effectiveTypeToConnectivityResult(info.effectiveType!);
}
if (info.type != null) {
return _typeToConnectivityResult(info.type);
return _typeToConnectivityResult(info.type!);
}
return ConnectivityResult.none;
}
Expand Down
Loading