Skip to content

Commit 4db0c6b

Browse files
authored
feat: add flutterTest (#2)
1 parent 1de845a commit 4db0c6b

File tree

11 files changed

+824
-22
lines changed

11 files changed

+824
-22
lines changed

.github/workflows/main.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ on:
1414

1515
jobs:
1616
build:
17-
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1
17+
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
18+
with:
19+
flutter_version: 2.10.3

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ build/
77
pubspec.lock
88

99
# Files related to tests
10-
coverage/
10+
coverage/
11+
.test_coverage.dart

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ Developed with 💙 by [Very Good Ventures][very_good_ventures_link] 🦄
1313

1414
A test runner for Flutter and Dart created by Very Good Ventures.
1515

16+
## Usage
17+
18+
```dart
19+
import 'package:very_good_test_runner/very_good_test_runner.dart';
20+
21+
void main() {
22+
flutterTest(workingDirectory: 'path/to/project').listen((TestEvent event) {
23+
// React to `TestEvent` instances.
24+
// See https://github.com/dart-lang/test/blob/master/pkgs/test/doc/json_reporter.md#json-reporter-protocol
25+
print(event);
26+
});
27+
}
28+
```
29+
1630
[ci_badge]: https://github.com/VeryGoodOpenSource/very_good_test_runner/workflows/very_good_test_runner/badge.svg
1731
[ci_link]: https://github.com/VeryGoodOpenSource/very_good_test_runner/actions
1832
[coverage_badge]: https://raw.githubusercontent.com/VeryGoodOpenSource/very_good_test_runner/main/coverage_badge.svg

analysis_options.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
include: package:very_good_analysis/analysis_options.2.4.0.yaml
1+
include: package:very_good_analysis/analysis_options.2.4.0.yaml

coverage_badge.svg

Lines changed: 18 additions & 18 deletions
Loading

lib/example/main.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// ignore_for_file: avoid_print
2+
3+
import 'package:very_good_test_runner/very_good_test_runner.dart';
4+
5+
void main() {
6+
// React to `TestEvent` instances.
7+
flutterTest().listen(print);
8+
}

lib/src/flutter_test_runner.dart

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
4+
import 'package:universal_io/io.dart';
5+
import 'package:very_good_test_runner/very_good_test_runner.dart';
6+
7+
/// Signature for `Process.start`.
8+
typedef StartProcess = Future<Process> Function(
9+
String executable,
10+
List<String> arguments, {
11+
String? workingDirectory,
12+
Map<String, String>? environment,
13+
bool includeParentEnvironment,
14+
bool runInShell,
15+
ProcessStartMode mode,
16+
});
17+
18+
/// Runs `flutter test` and returns a stream of [TestEvent]
19+
/// reported by the process.
20+
///
21+
/// ```dart
22+
/// void main() {
23+
/// // React to `TestEvent` instances.
24+
/// flutterTest().listen(print);
25+
/// }
26+
/// ```
27+
Stream<TestEvent> flutterTest({
28+
List<String>? arguments,
29+
String? workingDirectory,
30+
Map<String, String>? environment,
31+
bool runInShell = false,
32+
StartProcess startProcess = Process.start,
33+
}) {
34+
final controller = StreamController<TestEvent>();
35+
late StreamSubscription testEventSubscription;
36+
late StreamSubscription errorSubscription;
37+
late Future<Process> processFuture;
38+
39+
Future<void> _onListen() async {
40+
processFuture = startProcess(
41+
'flutter',
42+
['test', ...?arguments, '--reporter=json'],
43+
environment: environment,
44+
workingDirectory: workingDirectory,
45+
runInShell: runInShell,
46+
);
47+
final process = await processFuture;
48+
final errors = process.stderr.map((e) => utf8.decode(e).trim());
49+
final testEvents = process.stdout.mapToTestEvents();
50+
errorSubscription = errors.listen(controller.addError);
51+
testEventSubscription = testEvents.listen(
52+
controller.add,
53+
onError: controller.addError,
54+
onDone: controller.close,
55+
);
56+
}
57+
58+
Future<void> _onCancel() async {
59+
await controller.close();
60+
(await processFuture).kill();
61+
await errorSubscription.cancel();
62+
await testEventSubscription.cancel();
63+
}
64+
65+
controller
66+
..onListen = _onListen
67+
..onCancel = _onCancel;
68+
69+
return controller.stream;
70+
}
71+
72+
extension on Stream<List<int>> {
73+
Stream<TestEvent> mapToTestEvents() {
74+
return map(utf8.decode)
75+
.expand<String>((msg) sync* {
76+
for (final value in msg.split('\n')) {
77+
final trimmedValue = value.trim();
78+
if (trimmedValue.isNotEmpty) yield trimmedValue;
79+
}
80+
})
81+
.expand<Object?>((j) {
82+
try {
83+
return [json.decode(j)];
84+
} on FormatException {
85+
return [];
86+
}
87+
})
88+
.cast<Map<Object?, Object?>>()
89+
.map((json) => TestEvent.fromJson(Map<String, dynamic>.from(json)));
90+
}
91+
}

lib/very_good_test_runner.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// A test runner for Flutter and Dart created by Very Good Ventures.
12
library very_good_test_runner;
23

4+
export 'src/flutter_test_runner.dart' show flutterTest;
35
export 'src/models/models.dart';

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ environment:
88

99
dependencies:
1010
json_annotation: ^4.4.0
11+
universal_io: ^2.0.0
1112

1213
dev_dependencies:
1314
build_runner: ^2.0.0

0 commit comments

Comments
 (0)