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

Allow for specifying a particular device to use #53

Merged
merged 4 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion AutomatorServer/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ android {
minSdkVersion 26
targetSdkVersion 32
versionCode 1
versionName "0.0.8"
versionName "0.0.9"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Simple, easy-to-learn, Flutter-native UI testing framework eliminating
limitations of `flutter_driver`.

[![maestro_test on pub.dev][pub_badge_test]][pub_link_test]

[![maestro_cli on pub.dev][pub_badge_cli]][pub_link_cli]
[![code style][pub_badge_style]][pub_badge_link]

## CLI

Expand Down Expand Up @@ -34,7 +34,7 @@ $ maestro drive

## Package

The `maestro_test` package builds on top of `flutter_driver` to make it easy to
`maestro_test` package builds on top of `flutter_driver` to make it easy to
control the native device. It does this by using Android's
[UIAutomator][ui_automator] library.

Expand Down Expand Up @@ -103,9 +103,11 @@ git tag -a "maestro_cli-v0.0.4" -m "Release notes go here"

2. Push it! GitHub Actions will take care of the rest.

[pub_badge_test]: https://img.shields.io/pub/v/maestro_test.svg
[pub_badge_test]: https://img.shields.io/pub/v/maestro_test?label=maestro_test
[pub_link_test]: https://pub.dartlang.org/packages/maestro_test
[pub_badge_cli]: https://img.shields.io/pub/v/maestro_cli.svg
[pub_badge_cli]: https://img.shields.io/pub/v/maestro_cli?label=maestro_cli
[pub_badge_style]: https://img.shields.io/badge/style-leancode__lint-black
[pub_badge_link]: https://pub.dartlang.org/packages/lean_code_lint
[pub_link_cli]: https://pub.dartlang.org/packages/maestro_cli
[ui_automator]: https://developer.android.com/training/testing/other-components/ui-automator
[annotated_tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging#_annotated_tags
5 changes: 5 additions & 0 deletions packages/maestro_cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.0.9

- Add `--device` option for `maestro drive`, which allows you to specify the
device to use. Devices can be obtained using `adb devices`.

## 0.0.8

- Fix `maestro drive` on Windows crashing with ProcessException.
Expand Down
17 changes: 12 additions & 5 deletions packages/maestro_cli/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# maestro_cli

Command-line tool for [maestro_test][pub_link_test].

[![maestro_cli on pub.dev][pub_badge]][pub_link]
[![code style][pub_badge_style]][pub_badge_link]

Command-line tool to make working with [maestro_test][pub_link_test] easier.

## Installation

Expand All @@ -19,9 +20,11 @@ $ dart pub global activate maestro_cli
3. Go to `packages/maestro_cli`.
4. Run `dart pub global activate --source path .`

Now you can should be able to run `maestro` in your terminal. If you can't and
the error is something along the lines of "command not found", make sure that
you've added appropriate directories to PATH:
### Troubleshooting

If you can't run `maestro` from the terminal and the error is something along
the lines of "command not found", make sure that you've added appropriate
directories to PATH:

- on Unix-like systems, add `$HOME/.pub-cache/bin`
- on Windows, add `%USERPROFILE%\AppData\Local\Pub\Cache\bin`
Expand Down Expand Up @@ -57,3 +60,7 @@ Run `maestro bootstrap` to automatically do 1, 2, 3, 4, and most of 5.
[pub_badge]: https://img.shields.io/pub/v/maestro_cli.svg
[pub_link]: https://pub.dartlang.org/packages/maestro_cli
[pub_link_test]: https://pub.dartlang.org/packages/maestro_test
[pub_badge]: https://img.shields.io/pub/v/maestro_cli.svg
[pub_link]: https://pub.dartlang.org/packages/maestro_cli
[pub_badge_style]: https://img.shields.io/badge/style-leancode__lint-black
[pub_badge_link]: https://pub.dartlang.org/packages/lean_code_lint
18 changes: 14 additions & 4 deletions packages/maestro_cli/lib/src/commands/drive_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class DriveCommand extends Command<int> {
'driver',
abbr: 'd',
help: 'Dart file which starts flutter_driver.',
)
..addOption(
'device',
help: 'Serial number of ADB device to use.',
);
}

Expand Down Expand Up @@ -65,17 +69,23 @@ class DriveCommand extends Command<int> {
throw const FormatException('`driver` argument is not a string');
}

final device = argResults?['device'] as String?;

final options = MaestroDriveOptions(
host: host,
port: int.parse(portStr),
target: target,
driver: driver,
);

await adb.installApps();
await adb.forwardPorts(options.port);
await adb.runServer();
await flutter_driver.runTestsWithOutput(options.driver, options.target);
await adb.installApps(device: device);
await adb.forwardPorts(options.port, device: device);
await adb.runServer(device: device);
await flutter_driver.runTestsWithOutput(
options.driver,
options.target,
device: device,
);

return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/maestro_cli/lib/src/common/constants.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Version of Maestro CLI. Must be kept in sync with pubspec.yaml.
const version = '0.0.8';
const version = '0.0.9';

const maestroPackage = 'maestro_test';
const maestroCliPackage = 'maestro_cli';
Expand Down
24 changes: 18 additions & 6 deletions packages/maestro_cli/lib/src/external/adb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import 'dart:io';
import 'package:maestro_cli/src/common/common.dart';
import 'package:path/path.dart' as path;

Future<void> installApps() async {
Future<void> installApps({String? device}) async {
final progress1 = log.progress('Installing server');
try {
await _installApk(serverArtifactFile);
await _installApk(serverArtifactFile, device: device);
} catch (err) {
progress1.fail('Failed to install server');
rethrow;
Expand All @@ -16,7 +16,7 @@ Future<void> installApps() async {

final progress2 = log.progress('Installing instrumentation');
try {
await _installApk(instrumentationArtifactFile);
await _installApk(instrumentationArtifactFile, device: device);
} catch (err) {
progress2.fail('Failed to install instrumentation');
rethrow;
Expand All @@ -25,12 +25,16 @@ Future<void> installApps() async {
progress2.complete('Installed instrumentation');
}

Future<void> forwardPorts(int port) async {
Future<void> forwardPorts(int port, {String? device}) async {
final progress = log.progress('Forwarding ports');

final result = await Process.run(
'adb',
[
if (device != null) ...[
'-s',
device,
],
'forward',
'tcp:$port',
'tcp:$port',
Expand All @@ -46,14 +50,18 @@ Future<void> forwardPorts(int port) async {
progress.complete('Forwarded ports');
}

Future<void> runServer() async {
Future<void> runServer({String? device}) async {
final progress = log.progress('Starting instrumentation server');

Process process;
try {
process = await Process.start(
'adb',
[
if (device != null) ...[
'-s',
device,
],
'shell',
'am',
'instrument',
Expand Down Expand Up @@ -82,10 +90,14 @@ Future<void> runServer() async {
progress.complete('Started instrumentation server');
}

Future<void> _installApk(String name) async {
Future<void> _installApk(String name, {String? device}) async {
final result = await Process.run(
'adb',
[
if (device != null) ...[
'-s',
device,
],
'install',
path.join(artifactPath, name),
],
Expand Down
35 changes: 30 additions & 5 deletions packages/maestro_cli/lib/src/external/flutter_driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:maestro_cli/src/common/common.dart';

/// Runs flutter driver with the given [driver] and [target] and waits until the
/// drive is done.
Future<void> runTests(String driver, String target) async {
Future<void> runTests(String driver, String target, {String? device}) async {
log.info('Running tests...');

final res = await Process.run(
Expand All @@ -15,6 +15,10 @@ Future<void> runTests(String driver, String target) async {
driver,
'--target',
target,
if (device != null) ...[
'--device-id',
device,
],
],
runInShell: true,
);
Expand All @@ -28,7 +32,11 @@ Future<void> runTests(String driver, String target) async {
/// drive is done.
///
/// Prints standard output of "flutter drive".
Future<void> runTestsWithOutput(String driver, String target) async {
Future<void> runTestsWithOutput(
String driver,
String target, {
String? device,
}) async {
log.info('Running tests with output...');

final res = await Process.start(
Expand All @@ -39,11 +47,15 @@ Future<void> runTestsWithOutput(String driver, String target) async {
driver,
'--target',
target,
if (device != null) ...[
'--device-id',
device,
],
],
runInShell: true,
);

final sub = res.stdout.listen((msg) {
final stdOutSub = res.stdout.listen((msg) {
final text = 'driver: ${systemEncoding.decode(msg)}';
if (text.contains('I/flutter')) {
log.info(text);
Expand All @@ -52,6 +64,19 @@ Future<void> runTestsWithOutput(String driver, String target) async {
}
});

await res.exitCode;
await sub.cancel();
final stdErrSub = res.stderr.listen((msg) {
final text = 'driver: ${systemEncoding.decode(msg)}';
log.severe(text);
});

final exitCode = await res.exitCode;
await stdOutSub.cancel();
await stdErrSub.cancel();

final msg = 'flutter_driver exited with code $exitCode';
if (exitCode == 0) {
log.info(msg);
} else {
log.severe(msg);
}
}
2 changes: 1 addition & 1 deletion packages/maestro_cli/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: maestro_cli
description: CLI for Maestro.
version: 0.0.8
version: 0.0.9
homepage: https://github.com/leancodepl/maestro

environment:
Expand Down
74 changes: 57 additions & 17 deletions packages/maestro_test/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,68 @@
# maestro_test

TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
[![maestro_test on pub.dev][pub_badge]][pub_link]
[![code style][pub_badge_style]][pub_badge_link]

## Features
`maestro_test` package builds on top of `flutter_driver` to make it easy to
control the native device from Dart. It does this by using Android's
[UIAutomator][ui_automator] library.

TODO: List what your package can do. Maybe include images, gifs, or videos.
### Installation

## Getting started
Add `maestro_test` as a dev dependency in `pubspec.yaml`:

TODO: List prerequisites and provide or point to information on how to
start using the package.

## Usage
```
dev_dependencies:
maestro_test: ^0.0.3
```

TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
### Usage

```dart
const like = 'sample';
```
// integration_test/app_test.dart
import 'package:example/app.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:maestro_test/maestro_test.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Automator.init(verbose: true);
final automator = Automator.instance;

testWidgets(
"counter state is the same after going to Home and switching apps",
(WidgetTester tester) async {
Text findCounterText() {
return tester
.firstElement(find.byKey(const ValueKey('counterText')))
.widget as Text;
}

await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();

## Additional information
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
expect(findCounterText().data, '1');

await automator.pressHome();

await automator.pressDoubleRecentApps();

expect(findCounterText().data, '1');
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
expect(findCounterText().data, '2');

await automator.openNotifications();
},
);
}

```

TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.
[pub_badge]: https://img.shields.io/pub/v/maestro_test.svg
[pub_link]: https://pub.dartlang.org/packages/maestro_test
[pub_badge_style]: https://img.shields.io/badge/style-leancode__lint-black
[pub_badge_link]: https://pub.dartlang.org/packages/lean_code_lint