Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mock location #2547

Merged
merged 21 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from 17 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
8 changes: 8 additions & 0 deletions .github/workflows/prepare-e2e_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@ jobs:
run: .\gradlew.bat :patrol:ktlintFormat

- name: Build app with Gradle
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: .\gradlew.bat :app:assembleDebug

- name: Build app with Flutter tool
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: flutter build apk --debug

prepare-android-on-linux:
Expand Down Expand Up @@ -108,9 +112,13 @@ jobs:
run: ./gradlew :patrol:ktlintFormat

- name: Build app with Gradle
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: ./gradlew :app:assembleDebug

- name: Build app with Flutter tool
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: flutter build apk --debug

prepare-ios:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-android-emulator-webview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ jobs:
run: dart pub global activate --source path . && patrol

- name: Generate Gradle wrapper
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: flutter build apk --config-only

- name: Install ew-cli
Expand All @@ -104,6 +106,8 @@ jobs:
echo "INCLUDED_TESTS=$target_paths" >> "$GITHUB_ENV"

- name: patrol build android
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: patrol build android --target ${{ env.INCLUDED_TESTS }} --verbose

- name: Upload APKs to emulator.wtf and wait for tests to finish
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/test-android-emulator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ jobs:
run: dart pub global activate --source path . && patrol

- name: Generate Gradle wrapper
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: flutter build apk --config-only

- name: Install ew-cli
Expand Down Expand Up @@ -114,6 +116,8 @@ jobs:
echo "EXCLUDED_TESTS=$target_paths" >> "$GITHUB_ENV"

- name: patrol build android
env:
MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
run: |
patrol build android --exclude ${{ env.EXCLUDED_TESTS }} \
--dart-define-from-file=defines_1.json --dart-define-from-file=defines_2.json --dart-define-from-file=defines_3.env \
Expand Down
3 changes: 3 additions & 0 deletions dev/e2e_app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ android {
targetSdk 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [
mapsApiKey: System.getenv("MAPS_API_KEY") ?: ""
]
testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: "true"
}
Expand Down
18 changes: 10 additions & 8 deletions dev/e2e_app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
Expand All @@ -15,7 +16,6 @@

<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
Expand Down Expand Up @@ -48,17 +48,19 @@
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false"
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2"/>
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="${mapsApiKey}"/>
</application>
</manifest>
42 changes: 42 additions & 0 deletions dev/e2e_app/integration_test/mock_location_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'common.dart';

const _timeout = Duration(seconds: 5); // to avoid timeouts on CI

void main() {
patrol('mock location', ($) async {
await createApp($);

await $('Open map screen').scrollTo().tap();
await $.pumpAndSettle();

if (await $.native.isPermissionDialogVisible(timeout: _timeout)) {
await $.native.grantPermissionWhenInUse();
}

await $.pumpAndSettle();

await $.native.setMockLocation(55.2297, 21.0122);
await Future<void>.delayed(const Duration(milliseconds: 100));
await $.pumpAndSettle();
expect(await $('Location').waitUntilVisible(), findsOneWidget);
expect(await $('Latitude: 55.2297').waitUntilVisible(), findsOneWidget);
expect(await $('Longitude: 21.0122').waitUntilVisible(), findsOneWidget);
await Future<void>.delayed(const Duration(milliseconds: 5000));

await $.native.setMockLocation(55.5297, 21.0122);
await Future<void>.delayed(const Duration(milliseconds: 100));
await $.pumpAndSettle();
expect(await $('Location').waitUntilVisible(), findsOneWidget);
expect(await $('Latitude: 55.5297').waitUntilVisible(), findsOneWidget);
expect(await $('Longitude: 21.0122').waitUntilVisible(), findsOneWidget);
await Future<void>.delayed(const Duration(milliseconds: 5000));

await $.native.setMockLocation(55.7297, 21.0122);
await Future<void>.delayed(const Duration(milliseconds: 100));
await $.pumpAndSettle();
expect(await $('Location').waitUntilVisible(), findsOneWidget);
expect(await $('Latitude: 55.7297').waitUntilVisible(), findsOneWidget);
expect(await $('Longitude: 21.0122').waitUntilVisible(), findsOneWidget);
await Future<void>.delayed(const Duration(milliseconds: 5000));
});
}
2 changes: 1 addition & 1 deletion dev/e2e_app/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>14.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion dev/e2e_app/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
ios_deployment_target = '12.0'
ios_deployment_target = '14.0'
platform :ios, ios_deployment_target

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
Expand Down
21 changes: 20 additions & 1 deletion dev/e2e_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ PODS:
- Flutter
- geolocator_apple (1.2.0):
- Flutter
- Google-Maps-iOS-Utils (5.0.0):
- GoogleMaps (~> 8.0)
- google_maps_flutter_ios (0.0.1):
- Flutter
- Google-Maps-iOS-Utils (< 7.0, >= 5.0)
- GoogleMaps (< 10.0, >= 8.4)
- GoogleMaps (8.4.0):
- GoogleMaps/Maps (= 8.4.0)
- GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base
- patrol (0.0.1):
- CocoaAsyncSocket (~> 7.6)
- Flutter
Expand All @@ -25,13 +36,16 @@ DEPENDENCIES:
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
- patrol (from `.symlinks/plugins/patrol/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)

SPEC REPOS:
trunk:
- CocoaAsyncSocket
- Google-Maps-iOS-Utils
- GoogleMaps

EXTERNAL SOURCES:
app_links:
Expand All @@ -44,6 +58,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_timezone/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/ios"
google_maps_flutter_ios:
:path: ".symlinks/plugins/google_maps_flutter_ios/ios"
patrol:
:path: ".symlinks/plugins/patrol/darwin"
permission_handler_apple:
Expand All @@ -58,10 +74,13 @@ SPEC CHECKSUMS:
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282
geolocator_apple: 9bcea1918ff7f0062d98345d238ae12718acfbc1
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: e31555a04d1986ab130f2b9f24b6cdc861acc6d3
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
patrol: cf2cd48c7f3e5171610111994f7b466cd76d1f57
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4

PODFILE CHECKSUM: 5424a237bba16bfe751b9ef19da1cf80e4941454
PODFILE CHECKSUM: 66f59a059824e942cd3cb7be89914fda88260253

COCOAPODS: 1.16.2
6 changes: 3 additions & 3 deletions dev/e2e_app/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -692,7 +692,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -741,7 +741,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
3 changes: 3 additions & 0 deletions dev/e2e_app/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Flutter
import GoogleMaps
import UIKit

@main
Expand All @@ -7,6 +8,8 @@ import UIKit
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let mapsApiKey = ProcessInfo.processInfo.environment["MAPS_API_KEY"] ?? "YOUR_API_KEY"
GMSServices.provideAPIKey(mapsApiKey)
GeneratedPluginRegistrant.register(with: self)
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
Expand Down
9 changes: 9 additions & 0 deletions dev/e2e_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:app_links/app_links.dart';
import 'package:e2e_app/applink_screen.dart';
import 'package:e2e_app/loading_screen.dart';
import 'package:e2e_app/location_screen.dart';
import 'package:e2e_app/map_screen.dart';
import 'package:e2e_app/notifications_screen.dart';
import 'package:e2e_app/overlay_screen.dart';
import 'package:e2e_app/permissions_screen.dart';
Expand Down Expand Up @@ -202,6 +203,14 @@ class _ExampleHomePageState extends State<ExampleHomePage> {
),
child: const Text('Open location screen'),
),
TextButton(
onPressed: () async => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const MapScreen(),
),
),
child: const Text('Open map screen'),
),
TextButton(
onPressed: () async => Navigator.of(context).push(
MaterialPageRoute<void>(
Expand Down
77 changes: 77 additions & 0 deletions dev/e2e_app/lib/map_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapScreen extends StatefulWidget {
const MapScreen({super.key});

@override
State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
Position? position;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Map'),
),
body: Column(
children: [
const SizedBox(height: 10),
if (position == null)
const Text('Waiting for location...')
else ...[
Text('Location'),
const SizedBox(height: 10),
Text('Latitude: ${position?.latitude}'),
const SizedBox(height: 10),
Text('Longitude: ${position?.longitude}'),
const SizedBox(height: 10),
],
Expanded(
child: GoogleMap(
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 12,
),
onMapCreated: manageLocation,
),
),
],
),
);
}

Future<void> manageLocation(GoogleMapController controller) async {
LocationPermission permission;
permission = await Geolocator.requestPermission();

if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}

if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.',
);
}
Geolocator.getPositionStream().listen((position) {
setState(() {
this.position = position;
});

controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 12,
),
),
);
});
}
}
Loading
Loading