Skip to content

Commit

Permalink
sync develop with master (#1851)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartekpacia authored Nov 3, 2023
1 parent 6f05dab commit d1e2668
Show file tree
Hide file tree
Showing 21 changed files with 226 additions and 28 deletions.
26 changes: 13 additions & 13 deletions docs/ci/platforms.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
title: CI - Platforms
---

In this document we'll outline a few ways for running Patrol UI tests of Flutter
In this document, we'll outline a few ways to run Patrol UI tests of Flutter
apps.

Generally, the solutions for running UI tests of mobile apps can be divided into
2 groups:

- Device labs - platforms that provide access mobile devices in the cloud. You
- Device labs - platforms that provide access to mobile devices in the cloud. You
upload an app binary with tests to the device lab, which runs the tests and
reports the results back to you.

Expand All @@ -18,7 +18,7 @@ Generally, the solutions for running UI tests of mobile apps can be divided into
SDK tools, creating a virtual device, running tests, and collecting results.

There are quite a few solutions in each of these groups, and each is unique, but
generaly, **device labs trade flexibility for ease of use**. They're a good fit
generally, **device labs trade flexibility for ease of use**. They're a good fit
for most apps but make certain more complicated scenarios impossible.

# Device labs
Expand All @@ -39,7 +39,7 @@ See also:

### emulator.wtf

[emulator.wtf] is a fairly new solution created by the people at CodeMagic. It
[emulator.wtf] is a fairly new solution created by Madis Pink and Tauno Talimaa. It
claims to provide a 2-5x speedup compared to Firebase Test Lab, and 4-20x
speedup compared to spawning emulators on CI machines. It works similarly to
Firebase Test Lab - you upload your main apk, test apk, select emulators to run
Expand Down Expand Up @@ -70,27 +70,27 @@ our findings here.

Another popular device lab is [AWS Device Farm].

If you use-case is highly specific, you might want to build an in-house device
If your use-case is highly specific, you might want to build an in-house device
farm. A project that helps with this is [Simple Test Farm].

### Limitations

We mentioned above that device labs make certain scenarios impossible to
accomplish.

An example of such scenario scanning a QR code. One of the apps we worked on had
this feature, and we wanted to test it because it was critical part of the user
An example of such a scenario scanning a QR code. One of the apps we worked on had
this feature, and we wanted to test it because it was a critical part of the user
flow. When you have access to the shell filesystem (which you do have in the
"manual" approach, and don't have in the "device lab" approach), you can easily
[replace the scene that is visible in the camera's viewfinder][so_viewfinder].

This is no possible on device labs.
This is not possible on device labs.

# Traditional

### GitHub Actions

[GitHub Actions] is a very popular CI/CD platforms, especially among open-source
[GitHub Actions] is a very popular CI/CD platform, especially among open-source
projects thanks to unlimited minutes.

Unfortunately, running Flutter integration tests on GitHub Actions is not a
Expand All @@ -100,18 +100,18 @@ pleasant experience.

We used the [ReactiveCircus/android-emulator-runner] GitHub Action to run
Android emulator on GitHub Actions. Our takeaway is this: Running an Android
emulator on default GitHub Actions runner is a bad idea. It is slow to start and
emulator on the default GitHub Actions runner is a bad idea. It is slow to start and
unstable (apps crash randomly) and very slow. Really, really slow. We tried to
mitigate its instability by using [Test Butler], but it comes with its own
restrictions, most notably, it doesn't allow for Google Play Services.

**iOS**

We use the [futureware-tech/simulator-action] GitHub Action to run iOS simulator
on GitHub Actions is stable. But given that iOS simulator is just that – a
on GitHub Actions is stable. But given that the iOS simulator is just that – a
simulator, not an emulator – the range of cases it can be used for is reduced.
For example, there's no easy way to disable internet connection, which makes it
very hard to test behavior of an app when offline.
For example, there's no easy way to disable an internet connection, which makes it
very hard to test the behavior of an app when offline.

Bear in mind that to run an iOS simulator on GitHub Actions, you have to use a
macOS runner. 1 minute on macos-latest counts as 10 minutes on ubuntu-latest.
Expand Down
8 changes: 5 additions & 3 deletions docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Psst... Android is a bit easier to set up, so we recommend starting with it!

4. Make sure that the **iOS Deployment Target** of `RunnerUITests` within the
**Build Settings** section is the same as `Runner`.
The minimum supported **iOS Deployment Target** is `13.0`.
The minimum supported **iOS Deployment Target** is `11.0`.

![Xcode iOS deployment target](/assets/ios_deployment_target.png)

Expand Down Expand Up @@ -294,8 +294,10 @@ up. To run `integration_test/example_test.dart` on a local Android or iOS device
<Error>
To prevent issues during Patrol tests, please follow these guidelines:
1. Avoid using `WidgetsFlutterBinding.ensureInitialized` as Patrol already initializes its own binding. Using another one may lead to complications.
2. Refrain from overriding `FlutterError.onError` as it hinders Patrol's ability to listen to timeouts and ultimately causes it to never finish.
1. Do not call `IntegrationTestWidgetsFlutterBinding.ensureInitialized`.
Patrol automatically initializes its own test binding.
2. Do not modify the global `FlutterError.onError` callback. Patrol's
internals depend on it.
</Error>

### Going from here
Expand Down
3 changes: 3 additions & 0 deletions docs/patrol/finders/advanced.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /finders/advanced
---
3 changes: 3 additions & 0 deletions docs/patrol/finders/finders-setup.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /finders/finders-setup
---
3 changes: 3 additions & 0 deletions docs/patrol/finders/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /finders/overview
---
3 changes: 3 additions & 0 deletions docs/patrol/finders/usage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /finders/usage
---
3 changes: 3 additions & 0 deletions docs/patrol/native/advanced.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /native/advanced
---
3 changes: 3 additions & 0 deletions docs/patrol/native/feature-parity.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /native/feature-parity
---
3 changes: 3 additions & 0 deletions docs/patrol/native/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /native/overview
---
3 changes: 3 additions & 0 deletions docs/patrol/native/usage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect: /native/usage
---
4 changes: 4 additions & 0 deletions packages/patrol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ This version requires version 2.3.0-dev.1 of `patrol_cli` package.

- Add `patrolSetUp()` and `patrolTearDown()` (#1721)

## 2.3.2

- Add `PatrolFinder.longPress()` (#1825)

## 2.3.1

- Add support for iOS 11 and 12 (#1733)
Expand Down
5 changes: 2 additions & 3 deletions packages/patrol/example/integration_test/common.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';
import 'package:patrol_example/main.dart';
import 'package:patrol_example/main.dart' as app_main;

export 'package:flutter_test/flutter_test.dart';
export 'package:patrol/patrol.dart';
Expand All @@ -11,8 +11,7 @@ final _nativeAutomatorConfig = NativeAutomatorConfig(
);

Future<void> createApp(PatrolTester $) async {
await setUpTimezone();
await $.pumpWidget(ExampleApp());
await app_main.main();
}

void patrol(
Expand Down
2 changes: 1 addition & 1 deletion packages/patrol/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: b2bb71756d032256bcb4043384dd40772d5e6a93

COCOAPODS: 1.13.0
COCOAPODS: 1.14.2
7 changes: 4 additions & 3 deletions packages/patrol/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import 'package:patrol_example/webview_screen.dart';
import 'package:timezone/data/latest.dart' as tz_data;
import 'package:timezone/timezone.dart' as tz;

void main() {
runApp(const ExampleApp());
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await setUpTimezone();

setUpTimezone();
runApp(const ExampleApp());
}

Future<void> setUpTimezone() async {
Expand Down
4 changes: 4 additions & 0 deletions packages/patrol_cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

This version requires version 2.4.0-dev.4 of `patrol` package.

## 2.2.2

- Fix parsing `--dart-defines` when a value contains a comma (#1845)

## 2.2.1

- Fix bug with `test_bundle.dart` being sometimes garbled on Windows (#1797)
Expand Down
13 changes: 9 additions & 4 deletions packages/patrol_cli/lib/src/dart_defines_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ class DartDefinesReader {

Map<String, String> _parse(List<String> args) {
final map = <String, String>{};
var currentKey = ' ';
for (final arg in args) {
if (!arg.contains('=') && currentKey != ' ') {
map[currentKey] = '${map[currentKey]}, $arg';
continue;
}
final parts = arg.splitFirst('=');
final key = parts.first;
if (key.contains(' ')) {
throw FormatException('key "$key" contains whitespace');
currentKey = parts.first;
if (currentKey.contains(' ')) {
throw FormatException('key "$currentKey" contains whitespace');
}

final value = parts.elementAt(1);
map[key] = value;
map[currentKey] = value;
}

return map;
Expand Down
2 changes: 1 addition & 1 deletion packages/patrol_cli/lib/src/test_bundler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Future<void> main() async {
final didExploreTests = Completer<DartGroupEntry>();
final didExploreLifecycleCallbacks = Completer<void>();
// A special test to expore the hierarchy of groups and tests. This is a hack
// A special test to explore the hierarchy of groups and tests. This is a hack
// around https://github.com/dart-lang/test/issues/1998.
//
// This test must be the first to run. If not, the native side likely won't
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ void _test(Platform platform) {
final args = [
'EMAIL=email@wrong=domain.com',
r'PASSWORD="ny4ncat\n"',
'ATTRIBUTES=foo',
'bar',
];

expect(
reader.fromCli(args: args),
equals({
'EMAIL': 'email@wrong=domain.com',
'PASSWORD': r'"ny4ncat\n"',
'ATTRIBUTES': 'foo, bar',
}),
);
});
Expand Down
42 changes: 42 additions & 0 deletions packages/patrol_finders/lib/src/custom_finders/patrol_finder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,48 @@ class PatrolFinder extends MatchFinder {
);
}

/// Waits until this finder finds at least 1 visible widget and then makes
/// long press gesture on it.
///
/// Example:
/// ```dart
/// // long presses on the first widget having Key('createAccount')
/// await $(#createAccount).longPress();
/// ```
///
/// If the finder finds more than 1 widget, you can choose which one to make
/// long press on:
///
/// ```dart
/// // long presses on the third TextButton widget
/// await $(TextButton).at(2).longPress();
/// ```
///
/// After long press gesture this method automatically calls
/// [WidgetTester.pumpAndSettle]. If you want to disable this behavior,
/// set [settlePolicy] to [SettlePolicy.noSettle].
///
/// See also:
/// - [PatrolFinder.waitUntilVisible], which is used to wait for the widget
/// to appear
/// - [WidgetController.longPress]
Future<void> longPress({
@Deprecated('Use settlePolicy argument instead') bool? andSettle,
SettlePolicy? settlePolicy,
Duration? visibleTimeout,
Duration? settleTimeout,
}) async {
await tester.longPress(
this,
settlePolicy: chooseSettlePolicy(
andSettle: andSettle,
settlePolicy: settlePolicy,
),
visibleTimeout: visibleTimeout,
settleTimeout: settleTimeout,
);
}

/// Waits until this finder finds at least 1 visible widget and then enters
/// text into it.
///
Expand Down
49 changes: 49 additions & 0 deletions packages/patrol_finders/lib/src/custom_finders/patrol_tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,55 @@ class PatrolTester {
});
}

/// Waits until this finder finds at least 1 visible widget and then makes
/// long press gesture on it.
///
/// Example:
/// ```dart
/// // long presses on the first widget having Key('createAccount')
/// await $(#createAccount).longPress();
/// ```
///
/// If the finder finds more than 1 widget, you can choose which one to make
/// long press on:
///
/// ```dart
/// // long presses on the third TextButton widget
/// await $(TextButton).at(2).longPress();
/// ```
///
/// After long press gesture this method automatically calls
/// [WidgetTester.pumpAndSettle]. If you want to disable this behavior,
/// set [settlePolicy] to [SettlePolicy.noSettle].
///
/// See also:
/// - [PatrolFinder.waitUntilVisible], which is used to wait for the widget
/// to appear
/// - [WidgetController.longPress]
Future<void> longPress(
Finder finder, {
@Deprecated('Use settlePolicy argument instead') bool? andSettle,
SettlePolicy? settlePolicy,
Duration? visibleTimeout,
Duration? settleTimeout,
}) {
return TestAsyncUtils.guard(() async {
final resolvedFinder = await waitUntilVisible(
finder,
timeout: visibleTimeout,
);
await tester.longPress(resolvedFinder.first);
final settle = chooseSettlePolicy(
andSettle: andSettle,
settlePolicy: settlePolicy,
);
await _performPump(
settlePolicy: settle,
settleTimeout: settleTimeout,
);
});
}

/// Waits until [finder] finds at least 1 visible widget and then enters text
/// into it.
///
Expand Down
Loading

0 comments on commit d1e2668

Please sign in to comment.