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

[google_maps] Add initial google_maps tests #1409

Merged
merged 12 commits into from
Mar 29, 2019
Merged

[google_maps] Add initial google_maps tests #1409

merged 12 commits into from
Mar 29, 2019

Conversation

iskakaushik
Copy link
Contributor

@iskakaushik iskakaushik commented Mar 27, 2019

  • Also fixes a bug where we were not doing returning success when we update the markers.

- Also add a controller for querying things
- TODO: figureout a way to inject google maps api key
@iskakaushik iskakaushik requested a review from amirh as a code owner March 27, 2019 17:21
@iskakaushik iskakaushik changed the title Add initial google_maps tests [google_maps] Add initial google_maps tests Mar 27, 2019
@@ -129,4 +129,9 @@ class GoogleMapController {
'cameraUpdate': cameraUpdate._toJson(),
});
}

// TODO(iskakaushik): dart doc.
MethodChannel getMethodChannel() {
Copy link
Contributor

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), () {});
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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)

Copy link
Contributor

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":
Copy link
Contributor

@collinjackson collinjackson Mar 28, 2019

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],
Copy link
Contributor

@collinjackson collinjackson Mar 28, 2019

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);
Copy link
Contributor

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)

Copy link
Contributor Author

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}" />
Copy link
Contributor

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), () {});
Copy link
Contributor

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();
Copy link
Contributor

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)

Copy link
Contributor Author

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"];
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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 {
Copy link
Contributor

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?

Copy link
Contributor

@amirh amirh left a 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 {
Copy link
Contributor

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.

Copy link
Contributor

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?

@iskakaushik iskakaushik merged commit abb7734 into flutter:master Mar 29, 2019
@iskakaushik iskakaushik deleted the gmaps-test-3 branch March 29, 2019 20:22
julianscheel pushed a commit to jusst-engineering/plugins that referenced this pull request Mar 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants