Skip to content

Commit 95bb793

Browse files
authored
[google_maps] Prepares packages to endorse web. (#4064)
Prepares the google_maps packages to endorse the Web platform: * `[google_maps_flutter]` Changes `example/integration_test` to run in the web. * Splits the only file there in 3 for them to be slightly more manageable. * (Does not require publishing) * `[google_maps_flutter_platform_interface]` Adds a test coming from the core plugin to the unit tests of this package. * (Does not require publishing) * `[google_maps_flutter_web]` Changes to add an "inspector" object, and to conform with the tests in the core package. * Implements a `GoogleMapsInspectorPlatform` to allow integration tests to inspect parts of the internal state of a gmap. * Fires a `MapStyleException` when an invalid JSON is used in `setMapStyle` (was `FormatException` previously), to conform with the expected behavior in the core plugin tests. * (Requires publishing) ## Issues * Part of: flutter/flutter#80688
1 parent 6df913c commit 95bb793

File tree

18 files changed

+1558
-1278
lines changed

18 files changed

+1558
-1278
lines changed

packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart

Lines changed: 10 additions & 1201 deletions
Large diffs are not rendered by default.

packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart

Lines changed: 439 additions & 0 deletions
Large diffs are not rendered by default.

packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_inspector.dart

Lines changed: 536 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// This file contains shared definitions used across multiple test scenarios.
6+
7+
import 'package:flutter/foundation.dart';
8+
import 'package:flutter/material.dart';
9+
import 'package:flutter_test/flutter_test.dart';
10+
import 'package:google_maps_flutter/google_maps_flutter.dart';
11+
12+
/// Initial map center
13+
const LatLng kInitialMapCenter = LatLng(0, 0);
14+
15+
/// Initial zoom level
16+
const double kInitialZoomLevel = 5;
17+
18+
/// Initial camera position
19+
const CameraPosition kInitialCameraPosition =
20+
CameraPosition(target: kInitialMapCenter, zoom: kInitialZoomLevel);
21+
22+
/// True if the test is running in an iOS device
23+
final bool isIOS = defaultTargetPlatform == TargetPlatform.iOS;
24+
25+
/// True if the test is running in an Android device
26+
final bool isAndroid =
27+
defaultTargetPlatform == TargetPlatform.android && !kIsWeb;
28+
29+
/// True if the test is running in a web browser.
30+
const bool isWeb = kIsWeb;
31+
32+
/// Pumps a [map] widget in [tester] of a certain [size], then waits until it settles.
33+
Future<void> pumpMap(WidgetTester tester, GoogleMap map,
34+
[Size size = const Size.square(200)]) async {
35+
await tester.pumpWidget(wrapMap(map, size));
36+
await tester.pumpAndSettle();
37+
}
38+
39+
/// Wraps a [map] in a bunch of widgets so it renders in all platforms.
40+
///
41+
/// An optional [size] can be passed.
42+
Widget wrapMap(GoogleMap map, [Size size = const Size.square(200)]) {
43+
return MaterialApp(
44+
home: Scaffold(
45+
body: Center(
46+
child: SizedBox.fromSize(
47+
size: size,
48+
child: map,
49+
),
50+
),
51+
),
52+
);
53+
}
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:ui' as ui;
7+
8+
import 'package:flutter/foundation.dart';
9+
import 'package:flutter/material.dart';
10+
import 'package:flutter_test/flutter_test.dart';
11+
import 'package:google_maps_flutter/google_maps_flutter.dart';
12+
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
13+
import 'package:integration_test/integration_test.dart';
14+
15+
import 'shared.dart';
16+
17+
/// Integration Tests for the Tiles feature. These also use the [GoogleMapsInspectorPlatform].
18+
void main() {
19+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
20+
runTests();
21+
}
22+
23+
void runTests() {
24+
GoogleMapsFlutterPlatform.instance.enableDebugInspection();
25+
26+
final GoogleMapsInspectorPlatform inspector =
27+
GoogleMapsInspectorPlatform.instance!;
28+
29+
group('Tiles', () {
30+
testWidgets(
31+
'set tileOverlay correctly',
32+
(WidgetTester tester) async {
33+
final Completer<int> mapIdCompleter = Completer<int>();
34+
final TileOverlay tileOverlay1 = TileOverlay(
35+
tileOverlayId: const TileOverlayId('tile_overlay_1'),
36+
tileProvider: _DebugTileProvider(),
37+
zIndex: 2,
38+
transparency: 0.2,
39+
);
40+
41+
final TileOverlay tileOverlay2 = TileOverlay(
42+
tileOverlayId: const TileOverlayId('tile_overlay_2'),
43+
tileProvider: _DebugTileProvider(),
44+
zIndex: 1,
45+
visible: false,
46+
transparency: 0.3,
47+
fadeIn: false,
48+
);
49+
await tester.pumpWidget(
50+
Directionality(
51+
textDirection: TextDirection.ltr,
52+
child: GoogleMap(
53+
initialCameraPosition: kInitialCameraPosition,
54+
tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
55+
onMapCreated: (GoogleMapController controller) {
56+
mapIdCompleter.complete(controller.mapId);
57+
},
58+
),
59+
),
60+
);
61+
await tester.pumpAndSettle(const Duration(seconds: 3));
62+
63+
final int mapId = await mapIdCompleter.future;
64+
65+
final TileOverlay tileOverlayInfo1 = (await inspector
66+
.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
67+
final TileOverlay tileOverlayInfo2 = (await inspector
68+
.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!;
69+
70+
expect(tileOverlayInfo1.visible, isTrue);
71+
expect(tileOverlayInfo1.fadeIn, isTrue);
72+
expect(tileOverlayInfo1.transparency,
73+
moreOrLessEquals(0.2, epsilon: 0.001));
74+
expect(tileOverlayInfo1.zIndex, 2);
75+
76+
expect(tileOverlayInfo2.visible, isFalse);
77+
expect(tileOverlayInfo2.fadeIn, isFalse);
78+
expect(tileOverlayInfo2.transparency,
79+
moreOrLessEquals(0.3, epsilon: 0.001));
80+
expect(tileOverlayInfo2.zIndex, 1);
81+
},
82+
);
83+
84+
testWidgets(
85+
'update tileOverlays correctly',
86+
(WidgetTester tester) async {
87+
final Completer<int> mapIdCompleter = Completer<int>();
88+
final Key key = GlobalKey();
89+
final TileOverlay tileOverlay1 = TileOverlay(
90+
tileOverlayId: const TileOverlayId('tile_overlay_1'),
91+
tileProvider: _DebugTileProvider(),
92+
zIndex: 2,
93+
transparency: 0.2,
94+
);
95+
96+
final TileOverlay tileOverlay2 = TileOverlay(
97+
tileOverlayId: const TileOverlayId('tile_overlay_2'),
98+
tileProvider: _DebugTileProvider(),
99+
zIndex: 3,
100+
transparency: 0.5,
101+
);
102+
await tester.pumpWidget(
103+
Directionality(
104+
textDirection: TextDirection.ltr,
105+
child: GoogleMap(
106+
key: key,
107+
initialCameraPosition: kInitialCameraPosition,
108+
tileOverlays: <TileOverlay>{tileOverlay1, tileOverlay2},
109+
onMapCreated: (GoogleMapController controller) {
110+
mapIdCompleter.complete(controller.mapId);
111+
},
112+
),
113+
),
114+
);
115+
116+
final int mapId = await mapIdCompleter.future;
117+
118+
final TileOverlay tileOverlay1New = TileOverlay(
119+
tileOverlayId: const TileOverlayId('tile_overlay_1'),
120+
tileProvider: _DebugTileProvider(),
121+
zIndex: 1,
122+
visible: false,
123+
transparency: 0.3,
124+
fadeIn: false,
125+
);
126+
127+
await tester.pumpWidget(
128+
Directionality(
129+
textDirection: TextDirection.ltr,
130+
child: GoogleMap(
131+
key: key,
132+
initialCameraPosition: kInitialCameraPosition,
133+
tileOverlays: <TileOverlay>{tileOverlay1New},
134+
onMapCreated: (GoogleMapController controller) {
135+
fail('update: OnMapCreated should get called only once.');
136+
},
137+
),
138+
),
139+
);
140+
141+
await tester.pumpAndSettle(const Duration(seconds: 3));
142+
143+
final TileOverlay tileOverlayInfo1 = (await inspector
144+
.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!;
145+
final TileOverlay? tileOverlayInfo2 = await inspector
146+
.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId);
147+
148+
expect(tileOverlayInfo1.visible, isFalse);
149+
expect(tileOverlayInfo1.fadeIn, isFalse);
150+
expect(tileOverlayInfo1.transparency,
151+
moreOrLessEquals(0.3, epsilon: 0.001));
152+
expect(tileOverlayInfo1.zIndex, 1);
153+
154+
expect(tileOverlayInfo2, isNull);
155+
},
156+
);
157+
158+
testWidgets(
159+
'remove tileOverlays correctly',
160+
(WidgetTester tester) async {
161+
final Completer<int> mapIdCompleter = Completer<int>();
162+
final Key key = GlobalKey();
163+
final TileOverlay tileOverlay1 = TileOverlay(
164+
tileOverlayId: const TileOverlayId('tile_overlay_1'),
165+
tileProvider: _DebugTileProvider(),
166+
zIndex: 2,
167+
transparency: 0.2,
168+
);
169+
170+
await tester.pumpWidget(
171+
Directionality(
172+
textDirection: TextDirection.ltr,
173+
child: GoogleMap(
174+
key: key,
175+
initialCameraPosition: kInitialCameraPosition,
176+
tileOverlays: <TileOverlay>{tileOverlay1},
177+
onMapCreated: (GoogleMapController controller) {
178+
mapIdCompleter.complete(controller.mapId);
179+
},
180+
),
181+
),
182+
);
183+
184+
final int mapId = await mapIdCompleter.future;
185+
186+
await tester.pumpWidget(
187+
Directionality(
188+
textDirection: TextDirection.ltr,
189+
child: GoogleMap(
190+
key: key,
191+
initialCameraPosition: kInitialCameraPosition,
192+
onMapCreated: (GoogleMapController controller) {
193+
fail('OnMapCreated should get called only once.');
194+
},
195+
),
196+
),
197+
);
198+
199+
await tester.pumpAndSettle(const Duration(seconds: 3));
200+
final TileOverlay? tileOverlayInfo1 = await inspector
201+
.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId);
202+
203+
expect(tileOverlayInfo1, isNull);
204+
},
205+
);
206+
}, skip: isWeb /* Tiles not supported on the web */);
207+
}
208+
209+
class _DebugTileProvider implements TileProvider {
210+
_DebugTileProvider() {
211+
boxPaint.isAntiAlias = true;
212+
boxPaint.color = Colors.blue;
213+
boxPaint.strokeWidth = 2.0;
214+
boxPaint.style = PaintingStyle.stroke;
215+
}
216+
217+
static const int width = 100;
218+
static const int height = 100;
219+
static final Paint boxPaint = Paint();
220+
static const TextStyle textStyle = TextStyle(
221+
color: Colors.red,
222+
fontSize: 20,
223+
);
224+
225+
@override
226+
Future<Tile> getTile(int x, int y, int? zoom) async {
227+
final ui.PictureRecorder recorder = ui.PictureRecorder();
228+
final Canvas canvas = Canvas(recorder);
229+
final TextSpan textSpan = TextSpan(
230+
text: '$x,$y',
231+
style: textStyle,
232+
);
233+
final TextPainter textPainter = TextPainter(
234+
text: textSpan,
235+
textDirection: TextDirection.ltr,
236+
);
237+
textPainter.layout(
238+
maxWidth: width.toDouble(),
239+
);
240+
textPainter.paint(canvas, Offset.zero);
241+
canvas.drawRect(
242+
Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint);
243+
final ui.Picture picture = recorder.endRecording();
244+
final Uint8List byteData = await picture
245+
.toImage(width, height)
246+
.then((ui.Image image) =>
247+
image.toByteData(format: ui.ImageByteFormat.png))
248+
.then((ByteData? byteData) => byteData!.buffer.asUint8List());
249+
return Tile(width, height, byteData);
250+
}
251+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/bash
2+
# Copyright 2013 The Flutter Authors. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
6+
if pgrep -lf chromedriver > /dev/null; then
7+
echo "chromedriver is running."
8+
9+
if [ $# -eq 0 ]; then
10+
echo "No target specified, running all tests..."
11+
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_test.dart --target='{}'
12+
else
13+
echo "Running test target: $1..."
14+
set -x
15+
flutter drive -d web-server --web-port=7357 --browser-name=chrome --driver=test_driver/integration_test.dart --target=$1
16+
fi
17+
18+
else
19+
echo "chromedriver is not running."
20+
echo "Please, check the README.md for instructions on how to use run_test.sh"
21+
fi
22+

packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
// Copyright 2013 The Flutter Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
4-
5-
// ignore:unnecessary_import
6-
import 'dart:typed_data';
7-
import 'dart:ui';
8-
94
import 'package:flutter/foundation.dart';
5+
import 'package:flutter/material.dart';
106
import 'package:flutter_test/flutter_test.dart';
117

128
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
@@ -171,6 +167,26 @@ void main() {
171167
<dynamic>['fromAssetImage', 'some/path.png', 1.0]),
172168
isA<BitmapDescriptor>());
173169
});
170+
171+
test('mipmaps determines dpi', () async {
172+
const ImageConfiguration imageConfiguration = ImageConfiguration(
173+
devicePixelRatio: 3,
174+
);
175+
176+
final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage(
177+
imageConfiguration,
178+
'red_square.png',
179+
);
180+
final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage(
181+
imageConfiguration,
182+
'red_square.png',
183+
mipmaps: false,
184+
);
185+
186+
expect((mip.toJson() as List<dynamic>)[2], 1);
187+
expect((scaled.toJson() as List<dynamic>)[2], 3);
188+
});
189+
174190
test('name cannot be null or empty', () {
175191
expect(() {
176192
BitmapDescriptor.fromJson(<dynamic>['fromAssetImage', null, 1.0]);

packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.5.0
2+
3+
* **BREAKING CHANGE:** Fires a `MapStyleException` when an invalid JSON is used
4+
in `setMapStyle` (was `FormatException` previously).
5+
* Implements a `GoogleMapsInspectorPlatform` to allow integration tests to inspect
6+
parts of the internal state of a map.
7+
18
## 0.4.0+9
29

310
* Removes obsolete null checks on non-nullable values.

packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
// Mocks generated by Mockito 5.4.0 from annotations
1+
// Mocks generated by Mockito 5.4.1 from annotations
22
// in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart.
33
// Do not manually edit this file.
44

5+
// @dart=2.19
6+
57
// ignore_for_file: no_leading_underscores_for_library_prefixes
68
import 'package:google_maps/google_maps.dart' as _i2;
79
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'

0 commit comments

Comments
 (0)