From c49f37a3248ad26dc8eaf4a9cb6c9fbd96c299d0 Mon Sep 17 00:00:00 2001 From: Sai Krishna Date: Thu, 27 Jun 2024 15:42:22 +0530 Subject: [PATCH] Add support for drag and drop (#10) * add pumpAndTrySettle instead of pumpAndTry * Added support for drag and drop and also use pumpAndTrySettle --- demo-app/lib/screens/loader_screen.dart | 1 + server/lib/src/handler/gesture/drag_drop.dart | 19 +++++++ server/lib/src/models/api/drag_drop.dart | 22 ++++++++ .../src/models/api/generated/drag_drop.g.dart | 21 ++++++++ server/lib/src/runner.dart | 2 +- server/lib/src/server.dart | 3 ++ server/lib/src/utils/element_helper.dart | 50 +++++++++++++++++-- server/pubspec.yaml | 2 +- 8 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 server/lib/src/handler/gesture/drag_drop.dart create mode 100644 server/lib/src/models/api/drag_drop.dart create mode 100644 server/lib/src/models/api/generated/drag_drop.g.dart diff --git a/demo-app/lib/screens/loader_screen.dart b/demo-app/lib/screens/loader_screen.dart index 436d3c1..58c9d27 100644 --- a/demo-app/lib/screens/loader_screen.dart +++ b/demo-app/lib/screens/loader_screen.dart @@ -44,6 +44,7 @@ class _LoaderScreenState extends State { ), child: const Text( "Login", + key: ValueKey("loader_login_button"), style: TextStyle(color: Colors.white), ), ), diff --git a/server/lib/src/handler/gesture/drag_drop.dart b/server/lib/src/handler/gesture/drag_drop.dart new file mode 100644 index 0000000..5a32cd1 --- /dev/null +++ b/server/lib/src/handler/gesture/drag_drop.dart @@ -0,0 +1,19 @@ +import 'package:appium_flutter_server/src/handler/request/request_handler.dart'; +import 'package:appium_flutter_server/src/models/api/appium_response.dart'; +import 'package:appium_flutter_server/src/utils/element_helper.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shelf_plus/shelf_plus.dart'; +import 'package:appium_flutter_server/src/models/api/drag_drop.dart'; + +class DragAndDrop extends RequestHandler { + DragAndDrop(super.route); + + @override + Future handle(Request request) async { + var sessionId = getSessionId(request); + DragAndDropModel model = + DragAndDropModel.fromJson(await request.body.asJson); + await ElementHelper.dragAndDrop(model); + return AppiumResponse(sessionId, null); + } +} diff --git a/server/lib/src/models/api/drag_drop.dart b/server/lib/src/models/api/drag_drop.dart new file mode 100644 index 0000000..1abc2c6 --- /dev/null +++ b/server/lib/src/models/api/drag_drop.dart @@ -0,0 +1,22 @@ +import 'package:appium_flutter_server/src/models/api/find_element.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:appium_flutter_server/src/models/api/element.dart'; + +part 'generated/drag_drop.g.dart'; + +@JsonSerializable() +class DragAndDropModel { + ElementModel source; + ElementModel target; + int? dragDuration; + + DragAndDropModel( + {required this.source, + required this.target, + this.dragDuration}); + + factory DragAndDropModel.fromJson(Map json) => + _$DragAndDropModelFromJson(json); + + Map toJson() => _$DragAndDropModelToJson(this); +} diff --git a/server/lib/src/models/api/generated/drag_drop.g.dart b/server/lib/src/models/api/generated/drag_drop.g.dart new file mode 100644 index 0000000..46d0852 --- /dev/null +++ b/server/lib/src/models/api/generated/drag_drop.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of '../drag_drop.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DragAndDropModel _$DragAndDropModelFromJson(Map json) => + DragAndDropModel( + source: ElementModel.fromJson(json['source'] as Map), + target: ElementModel.fromJson(json['target'] as Map), + dragDuration: (json['dragDuration'] as num?)?.toInt(), + ); + +Map _$DragAndDropModelToJson(DragAndDropModel instance) => + { + 'source': instance.source, + 'target': instance.target, + 'dragDuration': instance.dragDuration, + }; diff --git a/server/lib/src/runner.dart b/server/lib/src/runner.dart index f1cd1ab..48cb4bd 100644 --- a/server/lib/src/runner.dart +++ b/server/lib/src/runner.dart @@ -30,7 +30,7 @@ void initializeTest({Widget? app, Function? callback}) async { var appInfo = await PackageInfo.fromPlatform(); // Need a better way to fetch this for automated release, this needs to be updated along with version bump // Can stay for now as it is not a breaking change - var serverVersion = '0.0.11'; + var serverVersion = '0.0.12'; FlutterDriver.instance .initialize(tester: tester, binding: binding, appInfo: appInfo, serverVersion: serverVersion); //await tester.pumpWidget(app); diff --git a/server/lib/src/server.dart b/server/lib/src/server.dart index e89afa5..49c6bb9 100644 --- a/server/lib/src/server.dart +++ b/server/lib/src/server.dart @@ -25,6 +25,7 @@ import 'package:shelf_plus/shelf_plus.dart' as shelf_plus; import 'package:appium_flutter_server/src/handler/clear.dart'; +import 'handler/gesture/drag_drop.dart'; import 'handler/long_press.dart'; enum HttpMethod { GET, POST, DELETE, PUT, PATCH } @@ -77,6 +78,8 @@ class FlutterServer { "/session//appium/gestures/double_click")); _registerPost(ScrollTillVisibleHandler( "/session//appium/gestures/scroll_till_visible")); + _registerPost(DragAndDrop( + "/session//appium/gestures/drag_drop")); /* Wait handlers */ _registerPost( diff --git a/server/lib/src/utils/element_helper.dart b/server/lib/src/utils/element_helper.dart index 5aaf86c..202234a 100644 --- a/server/lib/src/utils/element_helper.dart +++ b/server/lib/src/utils/element_helper.dart @@ -7,6 +7,7 @@ import 'package:appium_flutter_server/src/exceptions/flutter_automation_error.da import 'package:appium_flutter_server/src/internal/element_lookup_strategy.dart'; import 'package:appium_flutter_server/src/internal/flutter_element.dart'; import 'package:appium_flutter_server/src/logger.dart'; +import 'package:appium_flutter_server/src/models/api/drag_drop.dart'; import 'package:appium_flutter_server/src/models/api/gesture.dart'; import 'package:appium_flutter_server/src/models/api/find_element.dart'; import 'package:appium_flutter_server/src/models/session.dart'; @@ -71,7 +72,7 @@ class ElementHelper { static Future click(FlutterElement element) async { WidgetTester tester = _getTester(); await tester.tap(element.by); - await tester.pumpAndSettle(); + await pumpAndTrySettle(); } static Future setText(FlutterElement element, String text) async { @@ -117,7 +118,7 @@ class ElementHelper { await tester.pump(kDoubleTapMinTime); await tester.tapAt(Offset(bounds.left + doubleClickModel.offset!.x, bounds.top + doubleClickModel.offset!.y)); - await tester.pumpAndSettle(); + await pumpAndTrySettle(); } } }); @@ -128,7 +129,7 @@ class ElementHelper { await tester.tap(element.by); await tester.pump(kDoubleTapMinTime); await tester.tap(element.by); - await tester.pumpAndSettle(); + await pumpAndTrySettle(); } static Future longPress(GestureModel longPressModel) async { @@ -162,7 +163,7 @@ class ElementHelper { log("Click by offset $bounds"); await tester.longPressAt( Offset(longPressModel.offset!.x, longPressModel.offset!.y)); - await tester.pumpAndSettle(); + await pumpAndTrySettle(); } } }); @@ -444,12 +445,30 @@ class ElementHelper { : 'Timed out waiting for condition'); } if (Platform.isAndroid) { - await tester.pumpAndSettle(); + await pumpAndTrySettle(timeout: const Duration(milliseconds: 200)); } await Future.delayed(const Duration(milliseconds: 100)); } while (!(await predicate())); } + static Future dragAndDrop(DragAndDropModel model) async { + return TestAsyncUtils.guard(() async { + WidgetTester tester = _getTester(); + final String sourceElementId = model.source.id; + final String targetElementId = model.target.id; + Session session = FlutterDriver.instance.getSessionOrThrow()!; + FlutterElement sourceEl = await session.elementsCache.get(sourceElementId); + FlutterElement targetEl = await session.elementsCache.get(targetElementId); + final Offset sourceElementLocation = tester.getCenter(sourceEl.by); + final Offset targetElementLocation = tester.getCenter(targetEl.by); + final TestGesture gesture = await tester.startGesture(sourceElementLocation, pointer: 7); + await gesture.moveTo(targetElementLocation); + await tester.pump(); + await gesture.up(); + await tester.pump(); + }); + } + static Future scrollUntilVisible({ required FindElementModel finder, FindElementModel? scrollView, @@ -540,4 +559,25 @@ class ElementHelper { return const Uuid().v4(); } } + static Future pumpAndTrySettle({ + Duration duration = const Duration(milliseconds: 100), + EnginePhase phase = EnginePhase.sendSemanticsUpdate, + Duration timeout = const Duration(milliseconds: 200), + }) async { + try { + WidgetTester tester = _getTester(); + await tester.pumpAndSettle( + duration, + phase, + timeout, + ); + } on FlutterError catch (err) { + if (err.message == 'pumpAndSettle timed out') { + //This method ignores pumpAndSettle timeouts on purpose + } else { + rethrow; + } + } + } + } diff --git a/server/pubspec.yaml b/server/pubspec.yaml index abdb608..9e9e7aa 100644 --- a/server/pubspec.yaml +++ b/server/pubspec.yaml @@ -1,6 +1,6 @@ name: appium_flutter_server description: "Appium Flutter server using Integration Test package for testing Flutter apps with Appium" -version: 0.0.11 +version: 0.0.12 homepage: "https://github.com/AppiumTestDistribution/appium-flutter-server" environment: