feat(utils): added well know step and driver helper method to long press a widget

This commit is contained in:
Jon Samwell 2020-08-11 10:20:31 +10:00
parent 26dc2e4463
commit cc1196b2e2
12 changed files with 172 additions and 62 deletions

View File

@ -1,3 +1,12 @@
## [1.1.8+7] - 11/08/2020
- Added well know steps and a driver helper method to long press a widget
```
When I long press "controlKey" button
When I long press "controlKey" icon for 1500 milliseconds
```
## [1.1.8+6] - 05/08/2020 ## [1.1.8+6] - 05/08/2020
- Upgraded to latest Gherkin library version which fixes issues with non-alpha-numeric characters in multiline strings and comments https://github.com/jonsamwell/dart_gherkin/issues/14 https://github.com/jonsamwell/dart_gherkin/issues/15 https://github.com/jonsamwell/dart_gherkin/issues/16 - Upgraded to latest Gherkin library version which fixes issues with non-alpha-numeric characters in multiline strings and comments https://github.com/jonsamwell/dart_gherkin/issues/14 https://github.com/jonsamwell/dart_gherkin/issues/15 https://github.com/jonsamwell/dart_gherkin/issues/16

View File

@ -926,16 +926,19 @@ For convenience the library defines a number of pre-defined steps so you can get
| I restart the app | Restarts the app under test | `Then I restart the app` | | I restart the app | Restarts the app under test | `Then I restart the app` |
| I tap the back button | Taps the page default back button widget | `Then I tap the back button` | | I tap the back button | Taps the page default back button widget | `Then I tap the back button` |
| I expect a {string} that contains the text {string} to also contain the text {string} | Discovers a sibling based on its parent widget type and asserts that the both text string exist within the parent. | `Then I expect a "Row" that contains the text "X" to also contain the text "Y"` | | I expect a {string} that contains the text {string} to also contain the text {string} | Discovers a sibling based on its parent widget type and asserts that the both text string exist within the parent. | `Then I expect a "Row" that contains the text "X" to also contain the text "Y"` |
| I swipe [down\|left\|right\|up] by {int} pixels on the {string} | Swipes in a cardinal direction on a widget discovered by its key. | `Then I swipe up by 800 pixels on the "login_screen"`, `Then I swipe left by 200 pixels on the "dismissible_list_item"` | | I swipe [down\|left\|right\|up] by {int} pixels on the {string} | Swipes in a cardinal direction on a widget discovered by its key. | `Then I swipe up by 800 pixels on the "login_screen"` , `Then I swipe left by 200 pixels on the "dismissible_list_item"` |
| I swipe [down\|left\|right\|up] by {int} pixels on the on the [button\|element\|label\|field\|text\|widget\|dialog\|popup] that contains the text {string} | Swipes in a cardinal direction on a widget discovered by its test. | `Then I swipe left by 400 pixels on the widget that contains the text "Dismiss Me"` | | I swipe [down\|left\|right\|up] by {int} pixels on the on the [button\|element\|label\|field\|text\|widget\|dialog\|popup] that contains the text {string} | Swipes in a cardinal direction on a widget discovered by its test. | `Then I swipe left by 400 pixels on the widget that contains the text "Dismiss Me"` |
| I tap the [button\|element\|label\|field\|text\|widget] that contains the text {string} within the {string} | Taps a widget that contains the text within another widget. If the text is not visible, the ancestor will be scrolled. | `Then I tap the label that contains the text "Logout" within the "user_settings_list"` | | I tap the [button\|element\|label\|field\|text\|widget] that contains the text {string} within the {string} | Taps a widget that contains the text within another widget. If the text is not visible, the ancestor will be scrolled. | `Then I tap the label that contains the text "Logout" within the "user_settings_list"` |
| I tap the [button\|element\|label\|icon\|field\|text\|widget] of type {string} | Taps a widget of type. | `Then I tap the element of type "MaterialButton"`, `Then I tap the label of type "ListTile"`, `Then I tap the field of type "TextField"` | | I tap the [button\|element\|label\|icon\|field\|text\|widget] of type {string} | Taps a widget of type. | `Then I tap the element of type "MaterialButton"` , `Then I tap the label of type "ListTile"` , `Then I tap the field of type "TextField"` |
| I tap the [button\|element\|label\|icon\|field\|text\|widget] of type {string} within the {string} | Taps a widget of type within another widget. | `Then I tap the element of type "MaterialButton" within the "user_settings_list"` | | I tap the [button\|element\|label\|icon\|field\|text\|widget] of type {string} within the {string} | Taps a widget of type within another widget. | `Then I tap the element of type "MaterialButton" within the "user_settings_list"` |
| I tap the [button\|element\|label\|icon\|field\|text\|widget] that contains the text {string} | Taps a widget that contains text. | `Then I tap the label that contains the text "Logout"`, `Then I tap the button that contains the text "Sign up"`, `Then I tap the widget that contains the text "My User Profile"` | | I tap the [button\|element\|label\|icon\|field\|text\|widget] that contains the text {string} | Taps a widget that contains text. | `Then I tap the label that contains the text "Logout"` , `Then I tap the button that contains the text "Sign up"` , `Then I tap the widget that contains the text "My User Profile"` |
| I expect the text {string} to be [present\|absent] | Asserts the existence of text on the screen. | `Then I expect the text "Logout" to be present`, `But I expect the text "Signup" to be absent` | | I expect the text {string} to be [present\|absent] | Asserts the existence of text on the screen. | `Then I expect the text "Logout" to be present` , `But I expect the text "Signup" to be absent` |
| I expect the text {string} to be [present\|absent] within the {string} | Asserts the existence of text within a parent widget. | `Then I expect the text "Logout" to be present within the "user_settings_list"`, `But I expect the text "Signup" to be absent within the "login_screen"` | | I expect the text {string} to be [present\|absent] within the {string} | Asserts the existence of text within a parent widget. | `Then I expect the text "Logout" to be present within the "user_settings_list"` , `But I expect the text "Signup" to be absent within the "login_screen"` |
| I wait until the {string} is [present\absent] | Delays until a widget is present or absent. | `Then I wait until the "login_loading_indicator" is absent`, `And I wait until the "login_screen" is present` | | I wait until the {string} is [present\absent] | Delays until a widget is present or absent. | `Then I wait until the "login_loading_indicator" is absent` , `And I wait until the "login_screen" is present` |
| I wait until the [button\|element\|label\|icon\|field\|text\|widget] of type {string} is [present\absent] | Waits until a widget type is present or absent. | `Then I wait until the element of type "ProgressIndicator" is absent`, `And I wait until the button of type "MaterialButton" is present` | | I wait until the [button\|element\|label\|icon\|field\|text\|widget] of type {string} is [present\absent] | Waits until a widget type is present or absent. | `Then I wait until the element of type "ProgressIndicator" is absent` , `And I wait until the button of type "MaterialButton" is present` |
| I long press the {string} [button\|element\|label\|icon\|field\|text\|widget] | Scrolls into view and long presses the widget for 500 milliseconds. | `When I long press "controlKey" button` |
| I long press the {string} [button\|element\|label\|icon\|field\|text\|widget] without scrolling it into view | Long presses the widget for 500 milliseconds. | `When I long press "controlKey" button without scrolling it into view` |
| I long press the {string} [button\|element\|label\|icon\|field\|text\|widget] for {int} milliseconds | Scrolls into view and long presses the widget for the give number of milliseconds. | `When I long press "controlKey" button without scrolling it into view for 1500 milliseconds` |
#### Flutter Driver Utilities #### Flutter Driver Utilities

View File

@ -9,3 +9,7 @@ export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=C:\Google\flutter\bin\cache\artifacts\engine\ios" export "FLUTTER_FRAMEWORK_DIR=C:\Google\flutter\bin\cache\artifacts\engine\ios"
export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"

View File

@ -23,6 +23,7 @@ class MyHomePage extends StatefulWidget {
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
int _counter = 0; int _counter = 0;
bool hasLongPressedText = false;
void _incrementCounter() { void _incrementCounter() {
setState(() { setState(() {
@ -91,7 +92,24 @@ class _MyHomePageState extends State<MyHomePage> {
MaterialPageRoute(builder: (context) => PageTwo()), MaterialPageRoute(builder: (context) => PageTwo()),
); );
}, },
) ),
GestureDetector(
onLongPress: () {
setState(() {
hasLongPressedText = true;
});
},
child: Container(
color:
hasLongPressedText ? Colors.blueGrey : Colors.transparent,
child: Text(
hasLongPressedText
? 'Text has been long pressed!'
: 'Text that has not been long pressed',
key: const Key('longPressText'),
),
),
),
], ],
), ),
), ),

View File

@ -0,0 +1,6 @@
Feature: Interaction
Scenario: Widget can be long pressed
Given I expect the "longPressText" to be "Text that has not been long pressed"
When I long press the "longPressText" text
Then I expect the "longPressText" to be "Text has been long pressed!"

File diff suppressed because one or more lines are too long

View File

@ -27,6 +27,7 @@ import 'package:gherkin/gherkin.dart';
import 'package:glob/glob.dart'; import 'package:glob/glob.dart';
import 'steps/then_expect_widget_to_be_present_step.dart'; import 'steps/then_expect_widget_to_be_present_step.dart';
import 'steps/when_long_press_widget_step.dart';
class FlutterTestConfiguration extends TestConfiguration { class FlutterTestConfiguration extends TestConfiguration {
String _observatoryDebuggerUri; String _observatoryDebuggerUri;
@ -194,6 +195,9 @@ class FlutterTestConfiguration extends TestConfiguration {
WhenTapBackButtonWidget(), WhenTapBackButtonWidget(),
WhenTapWidget(), WhenTapWidget(),
WhenTapWidgetWithoutScroll(), WhenTapWidgetWithoutScroll(),
WhenLongPressWidget(),
WhenLongPressWidgetWithoutScroll(),
WhenLongPressWidgetForDuration(),
GivenOpenDrawer(), GivenOpenDrawer(),
WhenPauseStep(), WhenPauseStep(),
WhenFillFieldStep(), WhenFillFieldStep(),

View File

@ -0,0 +1,70 @@
import 'package:flutter_gherkin/src/flutter/flutter_world.dart';
import 'package:flutter_gherkin/src/flutter/utils/driver_utils.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:gherkin/gherkin.dart';
/// Long presses the widget found with the given control key.
///
/// Parameters:
/// 1 - {string} the control key
///
/// Examples:
///
/// `When I long press "controlKey" button`
/// `When I long press "controlKey" element`
/// `When I long press "controlKey" label`
/// `When I long press "controlKey" icon`
/// `When I long press "controlKey" field`
/// `When I long press "controlKey" text`
/// `When I long press "controlKey" widget`
StepDefinitionGeneric WhenLongPressWidget() {
return when1<String, FlutterWorld>(
RegExp(
r'I long press the {string} (?:button|element|label|icon|field|text|widget)$'),
(key, context) async {
final finder = find.byValueKey(key);
await context.world.driver.scrollIntoView(
finder,
);
await FlutterDriverUtils.longPress(
context.world.driver,
finder,
);
},
);
}
StepDefinitionGeneric WhenLongPressWidgetWithoutScroll() {
return when1<String, FlutterWorld>(
RegExp(
r'I long press the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
(key, context) async {
final finder = find.byValueKey(key);
await FlutterDriverUtils.tap(
context.world.driver,
finder,
);
},
);
}
StepDefinitionGeneric WhenLongPressWidgetForDuration() {
return when2<String, int, FlutterWorld>(
RegExp(
r'I long press the {string} (?:button|element|label|icon|field|text|widget) for {int} milliseconds$'),
(key, milliseconds, context) async {
final finder = find.byValueKey(key);
await context.world.driver.scrollIntoView(
finder,
);
await FlutterDriverUtils.longPress(
context.world.driver,
finder,
pressDuration: Duration(milliseconds: milliseconds),
);
},
);
}

View File

@ -70,6 +70,16 @@ class FlutterDriverUtils {
await FlutterDriverUtils.waitForFlutter(driver, timeout: timeout); await FlutterDriverUtils.waitForFlutter(driver, timeout: timeout);
} }
static Future<void> longPress(
FlutterDriver driver,
SerializableFinder finder, {
Duration pressDuration = const Duration(milliseconds: 500),
Duration timeout = const Duration(seconds: 30),
}) async {
await driver.scroll(finder, 0, 0, pressDuration, timeout: timeout);
await FlutterDriverUtils.waitForFlutter(driver, timeout: timeout);
}
/// Waits until the [condition] returns true /// Waits until the [condition] returns true
/// Will raise a complete with a [TimeoutException] if the /// Will raise a complete with a [TimeoutException] if the
/// condition does not return true with the timeout period. /// condition does not return true with the timeout period.

View File

@ -35,7 +35,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.1" version: "2.4.2"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -43,6 +43,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -57,13 +64,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "0.1.4"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.12" version: "1.14.13"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -77,14 +91,14 @@ packages:
name: coverage name: coverage
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.11" version: "0.14.0"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
@ -92,13 +106,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.2" version: "0.16.2"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.0" version: "5.2.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -161,13 +182,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.4" version: "3.1.4"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.12"
intl: intl:
dependency: transitive dependency: transitive
description: description:
@ -195,7 +209,7 @@ packages:
name: json_rpc_2 name: json_rpc_2
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -209,7 +223,7 @@ packages:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.6" version: "0.12.8"
meta: meta:
dependency: "direct main" dependency: "direct main"
description: description:
@ -224,13 +238,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6+3" version: "0.9.6+3"
multi_server_socket:
dependency: transitive
description:
name: multi_server_socket
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
node_interop: node_interop:
dependency: transitive dependency: transitive
description: description:
@ -265,7 +272,7 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.4" version: "1.7.0"
pedantic: pedantic:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -273,13 +280,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -300,7 +300,7 @@ packages:
name: process name: process
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.12" version: "3.0.13"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -308,13 +308,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.4" version: "1.4.4"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -375,7 +368,7 @@ packages:
name: stack_trace name: stack_trace
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.3" version: "1.9.5"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -410,28 +403,28 @@ packages:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.14.4" version: "1.15.2"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.15" version: "0.2.17"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.4" version: "0.3.10"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.6" version: "1.2.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -481,13 +474,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.3" version: "0.7.3"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@ -496,5 +482,5 @@ packages:
source: hosted source: hosted
version: "2.2.1" version: "2.2.1"
sdks: sdks:
dart: ">=2.7.0 <3.0.0" dart: ">=2.9.0-14.0.dev <3.0.0"
flutter: ">=1.13.0" flutter: ">=1.13.0"

View File

@ -1,6 +1,6 @@
name: flutter_gherkin name: flutter_gherkin
description: A Gherkin / Cucumber parser and test runner for Dart and Flutter description: A Gherkin / Cucumber parser and test runner for Dart and Flutter
version: 1.1.8+6 version: 1.1.8+7
homepage: https://github.com/jonsamwell/flutter_gherkin homepage: https://github.com/jonsamwell/flutter_gherkin
environment: environment:

View File

@ -23,7 +23,7 @@ void main() {
config.prepare(); config.prepare();
expect(config.stepDefinitions, isNotNull); expect(config.stepDefinitions, isNotNull);
expect(config.stepDefinitions.length, 20); expect(config.stepDefinitions.length, 23);
expect(config.customStepParameterDefinitions, isNotNull); expect(config.customStepParameterDefinitions, isNotNull);
expect(config.customStepParameterDefinitions.length, 2); expect(config.customStepParameterDefinitions.length, 2);
}); });
@ -36,7 +36,7 @@ void main() {
config.prepare(); config.prepare();
expect(config.stepDefinitions, isNotNull); expect(config.stepDefinitions, isNotNull);
expect(config.stepDefinitions.length, 21); expect(config.stepDefinitions.length, 24);
expect(config.stepDefinitions.elementAt(0), expect(config.stepDefinitions.elementAt(0),
(x) => x is MockStepDefinition); (x) => x is MockStepDefinition);
expect(config.customStepParameterDefinitions, isNotNull); expect(config.customStepParameterDefinitions, isNotNull);