Skip to content

Commit da0ca85

Browse files
committed
Add remote tester bridge for Flutter integration tests
Introduces the flet_integration_test Dart package and RemoteWidgetTester for TCP-based remote control of Flutter integration tests. Updates FletTestApp in Python to support a new 'remote' test mode, allowing tests to be driven without launching a Flet app. Refactors test setup and teardown logic to support both local and remote modes, and exposes RemoteTester in the Python SDK.
1 parent 64f7191 commit da0ca85

File tree

12 files changed

+693
-57
lines changed

12 files changed

+693
-57
lines changed

client/integration_test/app_test.dart

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import 'dart:io';
22

33
import 'package:flet_client/main.dart' as app;
4+
import 'package:flet_integration_test/flet_integration_test.dart';
45
import 'package:flutter/foundation.dart';
56
import 'package:flutter_test/flutter_test.dart';
67
import 'package:integration_test/integration_test.dart';
78

8-
import 'flutter_tester.dart';
9-
109
void main() {
1110
var binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1211

@@ -15,7 +14,20 @@ void main() {
1514
var dir = Directory.current.path;
1615
debugPrint("Current dir: $dir");
1716

18-
app.tester = FlutterWidgetTester(tester, binding);
17+
const testerServerUrl = String.fromEnvironment("FLET_TEST_SERVER_URL");
18+
FlutterWidgetTester? widgetTester;
19+
20+
if (testerServerUrl.isNotEmpty) {
21+
debugPrint("Connecting to remote tester at $testerServerUrl");
22+
widgetTester = await RemoteWidgetTester.connect(
23+
tester: tester,
24+
binding: binding,
25+
serverUri: Uri.parse(testerServerUrl),
26+
);
27+
} else {
28+
widgetTester = FlutterWidgetTester(tester, binding);
29+
app.tester = widgetTester;
30+
}
1931

2032
List<String> args = [];
2133
const fletTestAppUrl = String.fromEnvironment("FLET_TEST_APP_URL");
@@ -36,10 +48,10 @@ void main() {
3648
app.main(args);
3749

3850
await Future.delayed(const Duration(milliseconds: 500));
39-
await app.tester?.pump(duration: const Duration(seconds: 1));
40-
await app.tester
41-
?.pumpAndSettle(duration: const Duration(milliseconds: 100));
42-
await app.tester?.waitForTeardown();
51+
await widgetTester?.pump(duration: const Duration(seconds: 1));
52+
await widgetTester?.pumpAndSettle(
53+
duration: const Duration(milliseconds: 100));
54+
await widgetTester?.waitForTeardown();
4355
});
4456
});
4557
}

client/pubspec.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ packages:
313313
relative: true
314314
source: path
315315
version: "0.1.0"
316+
flet_integration_test:
317+
dependency: "direct dev"
318+
description:
319+
path: "../packages/flet_integration_test"
320+
relative: true
321+
source: path
322+
version: "0.1.0"
316323
flet_lottie:
317324
dependency: "direct main"
318325
description:

client/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ dependency_overrides:
8686
dev_dependencies:
8787
flutter_test:
8888
sdk: flutter
89+
flet_integration_test:
90+
path: ../packages/flet_integration_test
8991

9092
# The "flutter_lints" package below contains a set of recommended lints to
9193
# encourage good coding practices. The lint set provided by the package is
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
migrate_working_dir/
12+
13+
# IntelliJ related
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.idea/
18+
19+
# The .vscode folder contains launch configuration and tasks you configure in
20+
# VS Code which you may wish to be included in version control, so this line
21+
# is commented out by default.
22+
#.vscode/
23+
24+
# Flutter/Dart/Pub related
25+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
26+
/pubspec.lock
27+
**/doc/api/
28+
.dart_tool/
29+
.packages
30+
build/
31+
.flutter-plugins
32+
.flutter-plugins-dependencies
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export 'src/flutter_test_finder.dart';
2+
export 'src/flutter_tester.dart';
3+
export 'src/remote_widget_tester.dart';

client/integration_test/flutter_test_finder.dart renamed to packages/flet_integration_test/lib/src/flutter_test_finder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
44
class FlutterTestFinder extends TestFinder {
55
final Finder finder;
66

7-
FlutterTestFinder(this.finder) : super();
7+
FlutterTestFinder(this.finder);
88

99
@override
1010
int get count => finder.evaluate().length;

client/integration_test/flutter_tester.dart renamed to packages/flet_integration_test/lib/src/flutter_tester.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:typed_data';
23
import 'dart:ui';
34

45
import 'package:flet/flet.dart';
@@ -13,16 +14,20 @@ class FlutterWidgetTester implements Tester {
1314
final WidgetTester _tester;
1415
final IntegrationTestWidgetsFlutterBinding _binding;
1516
final lock = Lock();
16-
final Completer _teardown = Completer();
17+
final Completer _teardown = Completer<void>();
1718

1819
FlutterWidgetTester(this._tester, this._binding);
1920

21+
@protected
22+
IntegrationTestWidgetsFlutterBinding get binding => _binding;
23+
2024
@override
2125
Future<void> pumpAndSettle({Duration? duration}) async {
2226
await lock.acquire();
2327
try {
24-
await _tester
25-
.pumpAndSettle(duration ?? const Duration(milliseconds: 100));
28+
await _tester.pumpAndSettle(
29+
duration ?? const Duration(milliseconds: 100),
30+
);
2631
} finally {
2732
lock.release();
2833
}
@@ -60,7 +65,8 @@ class FlutterWidgetTester implements Tester {
6065
if (defaultTargetPlatform != TargetPlatform.android &&
6166
defaultTargetPlatform != TargetPlatform.iOS) {
6267
throw Exception(
63-
"Full app screenshots are only available on Android and iOS.");
68+
"Full app screenshots are only available on Android and iOS.",
69+
);
6470
}
6571
if (defaultTargetPlatform == TargetPlatform.android) {
6672
await _binding.convertFlutterSurfaceToImage();

0 commit comments

Comments
 (0)