-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[google_maps] Add initial google_maps tests #1409
Conversation
- Also add a controller for querying things - TODO: figureout a way to inject google maps api key
@@ -129,4 +129,9 @@ class GoogleMapController { | |||
'cameraUpdate': cameraUpdate._toJson(), | |||
}); | |||
} | |||
|
|||
// TODO(iskakaushik): dart doc. | |||
MethodChannel getMethodChannel() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename _channel
to channel
and use @visibleForTesting
so we aren't exposing this API to developers?
mapTestState.toggleCompass(); | ||
|
||
// This delay exists for platform channel propagation. | ||
await Future<void>.delayed(Duration(seconds: 1), () {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should make toggleCompass
a Future<void>
so we can await
it in tests and get reliable timing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that we ideally want for a rebuild event to occur before the next assert. using testWidget wrapper we would have pumped a new widget, but it's not easy to do this here. Will fix this in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably do something like:
Future<void> pumpFrame() async {
final Completer<void> completer = Completer<void>();
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
completer.complete(null);
});
SchedulerBinding.instance.scheduleFrame();
return completer;
}
We can even have our own pumpWidget for these driver tests that calls runApp and then pumpFrame.
@collinjackson do you think we should keep such a utility in a common place? it will definitely be useful for both the maps and webview plugins (or alternatively if we can get the WidgetTester working on these tests)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we should move it to a common place and I'm happy to take the lead on that.
result.success(null); | ||
break; | ||
} | ||
case "map#stateSnapshot": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it would be more consistent with other plugins to have a separate async MethodCall name for each property that you want to read off of the googleMap and expose these properties as a public API of the plugin. This gives developers a suite of useful API for getting for properties of the map that can also be used in integration testing.
final List<dynamic> stateSnapshot = | ||
await _channel.invokeMethod('map#stateSnapshot'); | ||
return GoogleMapStateSnapshot( | ||
compassEnabled: stateSnapshot[0], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's clearer to use named arguments rather than indexed arguments for plugin method calls. But, you might not need this class if you agree with my other feedback above.
@@ -199,6 +199,13 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { | |||
markersController.changeMarkers((List<Object>) markersToChange); | |||
Object markerIdsToRemove = call.argument("markerIdsToRemove"); | |||
markersController.removeMarkers((List<Object>) markerIdsToRemove); | |||
result.success(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bugfix? if so be clear about it in the PR description, changelog, etc (probably worth considering splitting this to a separate PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated the PR description.
@@ -13,7 +13,7 @@ | |||
android:value="@integer/google_play_services_version" /> | |||
<meta-data | |||
android:name="com.google.android.geo.API_KEY" | |||
android:value="YOUR KEY HERE" /> | |||
android:value="${mapsApiKey}" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment saying this should be replaced(for those running locally)
mapTestState.toggleCompass(); | ||
|
||
// This delay exists for platform channel propagation. | ||
await Future<void>.delayed(Duration(seconds: 1), () {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably do something like:
Future<void> pumpFrame() async {
final Completer<void> completer = Completer<void>();
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
completer.complete(null);
});
SchedulerBinding.instance.scheduleFrame();
return completer;
}
We can even have our own pumpWidget for these driver tests that calls runApp and then pumpFrame.
@collinjackson do you think we should keep such a utility in a common place? it will definitely be useful for both the maps and webview plugins (or alternatively if we can get the WidgetTester working on these tests)
compassEnabled = await testController.isCompassEnabled(); | ||
expect(compassEnabled, false); | ||
|
||
mapTestState.toggleCompass(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking just at this test case (e.g if I were coming here to debug a test failure), it's kind of hard to understand what's going on.
I tend to favor keeping test cases more verbose, and having repetition across test cases such that when you read the test case you know what it is doing without having to learn the common facilities shared across the test cases in this file.
In this case I'd prefer to see something like:
pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
initialCameraPosition: _kInitialCameraPosition,
compassEnabled: false,
onMapCreated: (GoogleMapController controller) {
widget._controllerCompleter.complete(controller);
},
),
));
compassEnabled = await testController.isCompassEnabled();
expect(compassEnabled, false);
pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
initialCameraPosition: _kInitialCameraPosition,
compassEnabled: true,
onMapCreated: (GoogleMapController controller) {
widget._controllerCompleter.complete(controller);
},
),
));
compassEnabled = await testController.isCompassEnabled();
expect(compassEnabled, true);
More verbose, but it's clear what the test is doing (also I believe this is the typical style of widget tests in the framework)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created test_widgets.dart
that has pumpWidget
, we can potentially move it to a shared location, but fits nicely into the framework pattern.
// Provide the GoogleMaps API key. | ||
[GMSServices provideAPIKey:@"YOUR KEY HERE"]; | ||
NSString* mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be confusing for users, what do you think about still using the key "YOUR KEY HERE" if the env variable is not set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking of making it so that in the future for local runs we require the api key to be set via env var. That way we do not need to edit these files, and flutter run will automatically pick them up. But for short term, i'm cool with making this be "YOUR KEY HERE"
.
google_maps_flutter: | ||
path: ../ | ||
flutter_driver: | ||
sdk: flutter | ||
test: any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depend on a specific version with caret notation
|
||
import 'package:flutter/services.dart'; | ||
|
||
class GoogleMapTestController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Controller feels a little weird here, as this is not controlling the map but rather allows inspecting it(TestController sounds to me like it's controlling the test). Maybe call it something like GoogleMapInspectors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo nit
@@ -4,8 +4,8 @@ | |||
|
|||
import 'package:flutter/services.dart'; | |||
|
|||
class GoogleMapTestController { | |||
GoogleMapTestController(this._channel); | |||
class GoogleMapInspector { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add a doc comment explaining the role of this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about why this is its own class. Why not put methods on the GoogleMapController
?
Uh oh!
There was an error while loading. Please reload this page.