blob: 625ecb355a310cfddb4cba5b324fe9a9f31b2747 [file] [log] [blame]
// Copyright 2017 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 'dart:async';
import 'package:integration_test/integration_test.dart';
import 'package:google_maps/google_maps.dart' as gmaps;
import 'package:google_maps_flutter_web/google_maps_flutter_web.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
class _MockCirclesController extends Mock implements CirclesController {}
class _MockPolygonsController extends Mock implements PolygonsController {}
class _MockPolylinesController extends Mock implements PolylinesController {}
class _MockMarkersController extends Mock implements MarkersController {}
class _MockGMap extends Mock implements gmaps.GMap {
final onClickController = StreamController<gmaps.MouseEvent>.broadcast();
@override
Stream<gmaps.MouseEvent> get onClick => onClickController.stream;
final onRightclickController = StreamController<gmaps.MouseEvent>.broadcast();
@override
Stream<gmaps.MouseEvent> get onRightclick => onRightclickController.stream;
final onBoundsChangedController = StreamController<dynamic>.broadcast();
@override
Stream<dynamic> get onBoundsChanged => onBoundsChangedController.stream;
final onIdleController = StreamController<dynamic>.broadcast();
@override
Stream<dynamic> get onIdle => onIdleController.stream;
}
/// Test Google Map Controller
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('GoogleMapController', () {
final int mapId = 33930;
GoogleMapController controller;
StreamController<MapEvent> stream;
// Creates a controller with the default mapId and stream controller, and any `options` needed.
GoogleMapController _createController({Map<String, dynamic> options}) {
return GoogleMapController(
mapId: mapId,
streamController: stream,
rawOptions: options ?? <String, dynamic>{});
}
setUp(() {
stream = StreamController<MapEvent>.broadcast();
});
group('construct/dispose', () {
setUp(() {
controller = _createController();
});
testWidgets('constructor creates widget', (WidgetTester tester) async {
expect(controller.widget, isNotNull);
expect(controller.widget.viewType, endsWith('$mapId'));
});
testWidgets('widget is cached when reused', (WidgetTester tester) async {
final first = controller.widget;
final again = controller.widget;
expect(identical(first, again), isTrue);
});
testWidgets('dispose closes the stream and removes the widget',
(WidgetTester tester) async {
controller.dispose();
expect(stream.isClosed, isTrue);
expect(controller.widget, isNull);
});
});
group('init', () {
_MockCirclesController circles;
_MockMarkersController markers;
_MockPolygonsController polygons;
_MockPolylinesController polylines;
_MockGMap map;
setUp(() {
circles = _MockCirclesController();
markers = _MockMarkersController();
polygons = _MockPolygonsController();
polylines = _MockPolylinesController();
map = _MockGMap();
});
testWidgets('listens to map events', (WidgetTester tester) async {
controller = _createController();
controller.debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);
expect(map.onClickController.hasListener, isFalse);
expect(map.onRightclickController.hasListener, isFalse);
expect(map.onBoundsChangedController.hasListener, isFalse);
expect(map.onIdleController.hasListener, isFalse);
controller.init();
expect(map.onClickController.hasListener, isTrue);
expect(map.onRightclickController.hasListener, isTrue);
expect(map.onBoundsChangedController.hasListener, isTrue);
expect(map.onIdleController.hasListener, isTrue);
});
testWidgets('binds geometry controllers to map\'s',
(WidgetTester tester) async {
controller = _createController();
controller.debugSetOverrides(
createMap: (_, __) => map,
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);
controller.init();
verify(circles.bindToMap(mapId, map));
verify(markers.bindToMap(mapId, map));
verify(polygons.bindToMap(mapId, map));
verify(polylines.bindToMap(mapId, map));
});
testWidgets('renders initial geometry', (WidgetTester tester) async {
controller = _createController(options: {
'circlesToAdd': [
{'circleId': 'circle-1'}
],
'markersToAdd': [
{'markerId': 'marker-1'}
],
'polygonsToAdd': [
{
'polygonId': 'polygon-1',
'points': [
[43.355114, -5.851333],
[43.354797, -5.851860],
[43.354469, -5.851318],
[43.354762, -5.850824],
],
},
],
'polylinesToAdd': [
{
'polylineId': 'polyline-1',
'points': [
[43.355114, -5.851333],
[43.354797, -5.851860],
[43.354469, -5.851318],
[43.354762, -5.850824],
],
},
],
});
controller.debugSetOverrides(
circles: circles,
markers: markers,
polygons: polygons,
polylines: polylines,
);
controller.init();
final capturedCircles =
verify(circles.addCircles(captureAny)).captured[0] as Set<Circle>;
final capturedMarkers =
verify(markers.addMarkers(captureAny)).captured[0] as Set<Marker>;
final capturedPolygons = verify(polygons.addPolygons(captureAny))
.captured[0] as Set<Polygon>;
final capturedPolylines = verify(polylines.addPolylines(captureAny))
.captured[0] as Set<Polyline>;
expect(capturedCircles.first.circleId.value, 'circle-1');
expect(capturedMarkers.first.markerId.value, 'marker-1');
expect(capturedPolygons.first.polygonId.value, 'polygon-1');
expect(capturedPolylines.first.polylineId.value, 'polyline-1');
});
group('Initialization options', () {
gmaps.MapOptions capturedOptions;
setUp(() {
capturedOptions = null;
});
testWidgets('translates initial options', (WidgetTester tester) async {
controller = _createController(options: {
'options': {
'mapType': 2,
'zoomControlsEnabled': true,
}
});
controller.debugSetOverrides(createMap: (_, options) {
capturedOptions = options;
return map;
});
controller.init();
expect(capturedOptions, isNotNull);
expect(capturedOptions.mapTypeId, gmaps.MapTypeId.SATELLITE);
expect(capturedOptions.zoomControl, true);
expect(capturedOptions.gestureHandling, 'auto',
reason:
'by default the map handles zoom/pan gestures internally');
});
testWidgets('disables gestureHandling with scrollGesturesEnabled false',
(WidgetTester tester) async {
controller = _createController(options: {
'options': {
'scrollGesturesEnabled': false,
}
});
controller.debugSetOverrides(createMap: (_, options) {
capturedOptions = options;
return map;
});
controller.init();
expect(capturedOptions, isNotNull);
expect(capturedOptions.gestureHandling, 'none',
reason:
'disabling scroll gestures disables all gesture handling');
});
testWidgets('disables gestureHandling with zoomGesturesEnabled false',
(WidgetTester tester) async {
controller = _createController(options: {
'options': {
'zoomGesturesEnabled': false,
}
});
controller.debugSetOverrides(createMap: (_, options) {
capturedOptions = options;
return map;
});
controller.init();
expect(capturedOptions, isNotNull);
expect(capturedOptions.gestureHandling, 'none',
reason:
'disabling scroll gestures disables all gesture handling');
});
testWidgets('does not set initial position if absent',
(WidgetTester tester) async {
controller = _createController();
controller.debugSetOverrides(createMap: (_, options) {
capturedOptions = options;
return map;
});
controller.init();
expect(capturedOptions, isNotNull);
expect(capturedOptions.zoom, isNull);
expect(capturedOptions.center, isNull);
});
testWidgets('sets initial position when passed',
(WidgetTester tester) async {
controller = _createController(options: {
'initialCameraPosition': {
'target': [43.308, -5.6910],
'zoom': 12,
'bearing': 0,
'tilt': 0,
}
});
controller.debugSetOverrides(createMap: (_, options) {
capturedOptions = options;
return map;
});
controller.init();
expect(capturedOptions, isNotNull);
expect(capturedOptions.zoom, 12);
expect(capturedOptions.center, isNotNull);
});
});
group('Traffic Layer', () {
testWidgets('by default is disabled', (WidgetTester tester) async {
controller = _createController();
controller.init();
expect(controller.trafficLayer, isNull);
});
testWidgets('initializes with traffic layer',
(WidgetTester tester) async {
controller = _createController(options: {
'options': {
'trafficEnabled': true,
}
});
controller.debugSetOverrides(createMap: (_, __) => map);
controller.init();
expect(controller.trafficLayer, isNotNull);
});
});
});
// These are the methods that are delegated to the gmaps.GMap object, that we can mock...
group('Map control methods', () {
_MockGMap map;
setUp(() {
map = _MockGMap();
controller = _createController();
controller.debugSetOverrides(createMap: (_, __) => map);
controller.init();
});
group('updateRawOptions', () {
testWidgets('can update `options`', (WidgetTester tester) async {
controller.updateRawOptions({
'mapType': 2,
});
final options = verify(map.options = captureAny).captured[0];
expect(options.mapTypeId, gmaps.MapTypeId.SATELLITE);
});
testWidgets('can turn on/off traffic', (WidgetTester tester) async {
expect(controller.trafficLayer, isNull);
controller.updateRawOptions({
'trafficEnabled': true,
});
expect(controller.trafficLayer, isNotNull);
controller.updateRawOptions({
'trafficEnabled': false,
});
expect(controller.trafficLayer, isNull);
});
});
group('viewport getters', () {
testWidgets('getVisibleRegion', (WidgetTester tester) async {
await controller.getVisibleRegion();
verify(map.bounds);
});
testWidgets('getZoomLevel', (WidgetTester tester) async {
when(map.zoom).thenReturn(10);
await controller.getZoomLevel();
verify(map.zoom);
});
});
group('moveCamera', () {
testWidgets('newLatLngZoom', (WidgetTester tester) async {
await (controller
.moveCamera(CameraUpdate.newLatLngZoom(LatLng(19, 26), 12)));
verify(map.zoom = 12);
final captured = verify(map.panTo(captureAny)).captured[0];
expect(captured.lat, 19);
expect(captured.lng, 26);
});
});
group('map.projection methods', () {
// These are too much for dart mockito, can't mock:
// map.projection.method() (in Javascript ;) )
});
});
// These are the methods that get forwarded to other controllers, so we just verify calls.
group('Pass-through methods', () {
setUp(() {
controller = _createController();
});
testWidgets('updateCircles', (WidgetTester tester) async {
final mock = _MockCirclesController();
controller.debugSetOverrides(circles: mock);
final previous = {
Circle(circleId: CircleId('to-be-updated')),
Circle(circleId: CircleId('to-be-removed')),
};
final current = {
Circle(circleId: CircleId('to-be-updated'), visible: false),
Circle(circleId: CircleId('to-be-added')),
};
controller.updateCircles(CircleUpdates.from(previous, current));
verify(mock.removeCircles({
CircleId('to-be-removed'),
}));
verify(mock.addCircles({
Circle(circleId: CircleId('to-be-added')),
}));
verify(mock.changeCircles({
Circle(circleId: CircleId('to-be-updated'), visible: false),
}));
});
testWidgets('updateMarkers', (WidgetTester tester) async {
final mock = _MockMarkersController();
controller.debugSetOverrides(markers: mock);
final previous = {
Marker(markerId: MarkerId('to-be-updated')),
Marker(markerId: MarkerId('to-be-removed')),
};
final current = {
Marker(markerId: MarkerId('to-be-updated'), visible: false),
Marker(markerId: MarkerId('to-be-added')),
};
controller.updateMarkers(MarkerUpdates.from(previous, current));
verify(mock.removeMarkers({
MarkerId('to-be-removed'),
}));
verify(mock.addMarkers({
Marker(markerId: MarkerId('to-be-added')),
}));
verify(mock.changeMarkers({
Marker(markerId: MarkerId('to-be-updated'), visible: false),
}));
});
testWidgets('updatePolygons', (WidgetTester tester) async {
final mock = _MockPolygonsController();
controller.debugSetOverrides(polygons: mock);
final previous = {
Polygon(polygonId: PolygonId('to-be-updated')),
Polygon(polygonId: PolygonId('to-be-removed')),
};
final current = {
Polygon(polygonId: PolygonId('to-be-updated'), visible: false),
Polygon(polygonId: PolygonId('to-be-added')),
};
controller.updatePolygons(PolygonUpdates.from(previous, current));
verify(mock.removePolygons({
PolygonId('to-be-removed'),
}));
verify(mock.addPolygons({
Polygon(polygonId: PolygonId('to-be-added')),
}));
verify(mock.changePolygons({
Polygon(polygonId: PolygonId('to-be-updated'), visible: false),
}));
});
testWidgets('updatePolylines', (WidgetTester tester) async {
final mock = _MockPolylinesController();
controller.debugSetOverrides(polylines: mock);
final previous = {
Polyline(polylineId: PolylineId('to-be-updated')),
Polyline(polylineId: PolylineId('to-be-removed')),
};
final current = {
Polyline(polylineId: PolylineId('to-be-updated'), visible: false),
Polyline(polylineId: PolylineId('to-be-added')),
};
controller.updatePolylines(PolylineUpdates.from(previous, current));
verify(mock.removePolylines({
PolylineId('to-be-removed'),
}));
verify(mock.addPolylines({
Polyline(polylineId: PolylineId('to-be-added')),
}));
verify(mock.changePolylines({
Polyline(polylineId: PolylineId('to-be-updated'), visible: false),
}));
});
testWidgets('infoWindow visibility', (WidgetTester tester) async {
final mock = _MockMarkersController();
controller.debugSetOverrides(markers: mock);
final markerId = MarkerId('marker-with-infowindow');
controller.showInfoWindow(markerId);
verify(mock.showMarkerInfoWindow(markerId));
controller.hideInfoWindow(markerId);
verify(mock.hideMarkerInfoWindow(markerId));
controller.isInfoWindowShown(markerId);
verify(mock.isInfoWindowShown(markerId));
});
});
});
}