Skip to content
Open
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
12 changes: 9 additions & 3 deletions packages/widget_toolkit/example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion flutter.compileSdkVersion
compileSdk 34
ndkVersion flutter.ndkVersion

compileOptions {
Expand All @@ -47,10 +47,12 @@ android {
applicationId "com.primeholding.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: "true"
}

buildTypes {
Expand All @@ -60,12 +62,16 @@ android {
signingConfig signingConfigs.debug
}
}
testOptions {
execution "ANDROIDX_TEST_ORCHESTRATOR"
}
}

flutter {
source '../..'
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
androidTestUtil "androidx.test:orchestrator:1.4.2"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.primeholding.example; // replace "com.example.myapp" with your app's package

import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import pl.leancode.patrol.PatrolJUnitRunner;

@RunWith(Parameterized.class)
public class MainActivityTest {
@Parameters(name = "{0}")
public static Object[] testCases() {
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
// replace "MainActivity.class" with "io.flutter.embedding.android.FlutterActivity.class"
// if your AndroidManifest is using: android:name="io.flutter.embedding.android.FlutterActivity"
instrumentation.setUp(MainActivity.class);
instrumentation.waitForPatrolAppService();
return instrumentation.listDartTests();
}

public MainActivityTest(String dartTestName) {
this.dartTestName = dartTestName;
}

private final String dartTestName;

@Test
public void runDartTest() {
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
instrumentation.runDartTest(dartTestName);
}
}
4 changes: 2 additions & 2 deletions packages/widget_toolkit/example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.8.21'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:example/main.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

export 'package:flutter/foundation.dart';
export 'package:flutter_test/flutter_test.dart';
export 'package:patrol/patrol.dart';

class BuildApp {
late PatrolIntegrationTester $;

BuildApp(this.$);

Future<void> buildApp() async {
final FlutterExceptionHandler? originalOnError = FlutterError.onError;

main();

await $.pumpAndSettle();
FlutterError.onError = originalOnError;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'build_app.dart';

class ConfigParams {
const ConfigParams();

///General PatrolTesterConfig parameters
static const generalExistsTimeout = Duration(seconds: 10);
static const generalVisibleTimeout = Duration(seconds: 10);
static const generalSettleTimeout = Duration(seconds: 10);
static const generalSettlePolicy = SettlePolicy.settle;

///General NativeAutomatorConfig parameters
static const generalConnectionTimeout = Duration(seconds: 60);
static const generalFindTimeout = Duration(seconds: 10);

///Specific widget-related PatrolTesterConfig parameters
static const pageVisibleTimeout = Duration(seconds: 15);
static const mobileOnboardingScanQRTimeout = Duration(minutes: 1);
static const dashboardAccountCardScrollStep = 370.0;
static const dashboardAccountCardMaxScrolls = 1;
static const utilityBillsTabsScrollStep = 500.0;
static const utilityBillsItemsListScrollStep = 300.0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'build_app.dart';
import 'config_params.dart';

class PatrolBaseConfig {
// The default values for PatrolTesterConfig fields are set at ConfigParams class.
// All these global values can be overridden at its Specific widget-related
// parameters section. More info -> at the class documentation.
PatrolTesterConfig customPatrolTesterConfig() {
PatrolTesterConfig patrolTesterConfig = const PatrolTesterConfig(
existsTimeout: ConfigParams.generalExistsTimeout,
visibleTimeout: ConfigParams.generalVisibleTimeout,
settleTimeout: ConfigParams.generalSettleTimeout,
settlePolicy: ConfigParams.generalSettlePolicy);
return patrolTesterConfig;
}

// Patrol NativeAutomatorConfig has far more options. Here only the most
// common ones are listed. More info -> at the class documentation.
// The default values are set at ConfigParams class.
NativeAutomatorConfig customNativeAutomatorConfig() {
NativeAutomatorConfig nativeAutomatorConfig = const NativeAutomatorConfig(
connectionTimeout: ConfigParams.generalConnectionTimeout,
findTimeout: ConfigParams.generalFindTimeout);
return nativeAutomatorConfig;
}

void patrol(
String description,
Future<void> Function(PatrolIntegrationTester) callback, {
bool? skip,
}) {
patrolTest(
description,
callback,
config: customPatrolTesterConfig(),
nativeAutomatorConfig: customNativeAutomatorConfig(),
skip: skip,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:example/keys_testing/base_page_keys.dart' as keys;
import 'package:patrol/patrol.dart';

abstract class BasePage {
late PatrolIntegrationTester $;

BasePage(this.$);

Future<void> tapWidget(var widget) async => $(widget).tap();

Future<void> setInputField(var widget, String inputText) async =>
$(widget).enterText(inputText);

Future<void> tapNextButton() async => $(keys.nextButton).tap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:example/keys_testing/edit_field_page_keys.dart' as keys;

import 'base_page.dart';

class EditFieldsPage extends BasePage {
EditFieldsPage(super.$);

Future<void> tapSaveButton() async => tapWidget(keys.saveButton);

Future<void> tapFirstNameDialog() async => tapWidget(keys.firstNameDialog);

Future<void> setFirstName(String firstName) async =>
setInputField(keys.firstNameTextField, firstName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import '../configuration/build_app.dart';
import '../configuration/patrol_base_config.dart';
import '../pages/edit_fields_page.dart';

void main() {
final patrolBaseConfig = PatrolBaseConfig();

patrolBaseConfig.patrol('TextFieldDialog debug test', ($) async {
const testInput = 'Test';

final editFieldsPage = EditFieldsPage($);

await BuildApp($).buildApp();

await editFieldsPage.tapNextButton();
await editFieldsPage.tapNextButton();
await editFieldsPage.tapFirstNameDialog();
await editFieldsPage.setFirstName(testInput);
await editFieldsPage.tapSaveButton();
//Check that the keyboard is displayed
await Future.delayed(const Duration(seconds: 30));
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import 'package:flutter/cupertino.dart';

const nextButton = Key('basePageNextButton');
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:flutter/cupertino.dart';

const firstNameDialog = Key('editFieldsPageFirstNameDialog');
const saveButton = Key('editFieldsPageSaveButton');
const firstNameTextField = Key('editFieldsPageFirstNameTextField');
7 changes: 7 additions & 0 deletions packages/widget_toolkit/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_rx_bloc/rx_form.dart';
import 'package:widget_toolkit/widget_toolkit.dart';

import 'keys_testing/base_page_keys.dart' as base_page_keys;
import 'keys_testing/edit_field_page_keys.dart' as edit_field_page_keys;

void main() {
runApp(const MyApp());
}
Expand Down Expand Up @@ -82,6 +85,7 @@ class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(title), actions: [
IconButton(
key: base_page_keys.nextButton,
onPressed: () {
pageController.animateToPage(nextPageIndex,
duration: const Duration(milliseconds: 800),
Expand Down Expand Up @@ -343,6 +347,9 @@ class EditFieldsPage extends StatelessWidget {
WidgetSection(
description: 'TextFieldDialog',
child: TextFieldDialog<String>(
textFormFieldKey: edit_field_page_keys.firstNameTextField,
saveButtonKey: edit_field_page_keys.saveButton,
key: edit_field_page_keys.firstNameDialog,
translateError: (error) =>
TranslateErrorUtil.translateError<String>(error, context),
label: 'First Name',
Expand Down
7 changes: 7 additions & 0 deletions packages/widget_toolkit/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ dev_dependencies:
flutter_lints: ^2.0.1
flutter_test:
sdk: flutter
patrol: ^3.6.1
test: any

patrol:
app_name: example
android:
package_name: com.primeholding.example

dependency_overrides:
widget_toolkit:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class InputTextField extends StatefulWidget {
this.suffixIcon,
this.obBlurCallback,
this.keyBoardType = TextInputType.text,
this.textFormFieldKey,
super.key,
});

Expand All @@ -54,6 +55,7 @@ class InputTextField extends StatefulWidget {
final Color? defaultValueColor;
final Widget? suffixIcon;
final VoidCallback? obBlurCallback;
final Key? textFormFieldKey;

@override
InputTextFieldState createState() => InputTextFieldState();
Expand Down Expand Up @@ -205,6 +207,7 @@ class InputTextFieldState extends State<InputTextField> {
height: context.textFieldDialogTheme.spacingXSS,
),
TextFormField(
key: widget.textFormFieldKey,
focusNode: _focusNode,
keyboardType: widget.keyBoardType,
onEditingComplete: () => widget.obBlurCallback?.call(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class TextFieldDialog<T> extends StatefulWidget {
this.editFieldType = EditFieldType.editfield,
this.modalConfiguration = const TextFieldModalConfiguration(),
this.enabled = true,
this.textFormFieldKey,
this.saveButtonKey,
super.key,
}) : assert(editFieldCustomIcon == null ||
editFieldCustomIcon is IconData ||
Expand All @@ -114,6 +116,8 @@ class TextFieldDialog<T> extends StatefulWidget {
final EditFieldType editFieldType;
final bool enabled;
final TextFieldModalConfiguration modalConfiguration;
final Key? textFormFieldKey;
final Key? saveButtonKey;

@override
State<TextFieldDialog<T>> createState() => _TextFieldDialogState<T>();
Expand Down Expand Up @@ -187,6 +191,8 @@ class _TextFieldDialogState<T> extends State<TextFieldDialog<T>> {
initialValue: _value,
).providers,
child: TextFieldDialogPage<T>(
textFormFieldKey: widget.textFormFieldKey,
saveButtonKey: widget.saveButtonKey,
translateError: widget.translateError,
callback: (value) {
setState(() => _value = value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class TextFieldDialogPage<T> extends StatelessWidget {
this.isMultiLinedInputField = false,
this.dialogHasBottomPadding = false,
this.maxLines,
this.textFormFieldKey,
this.saveButtonKey,
super.key,
});

Expand All @@ -56,6 +58,8 @@ class TextFieldDialogPage<T> extends StatelessWidget {
final bool dialogHasBottomPadding;
final int? maxLines;
final Function(Object error) translateError;
final Key? textFormFieldKey;
final Key? saveButtonKey;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -96,6 +100,7 @@ class TextFieldDialogPage<T> extends StatelessWidget {
onChanged: (bloc, value) => bloc.events.setText(value),
cursorBehaviour: RxTextFormFieldCursorBehaviour.end,
builder: (fieldState) => InputTextField(
textFormFieldKey: textFormFieldKey,
isFocused: true,
keyBoardType: keyBoardType,
label: label,
Expand All @@ -118,6 +123,7 @@ class TextFieldDialogPage<T> extends StatelessWidget {
child: RxBlocBuilder<TextFieldDialogBlocType, Result<T>>(
state: (bloc) => bloc.states.submittedValue as Stream<Result<T>>,
builder: (context, snapshot, bloc) => GradientFillButton(
buttonKey: saveButtonKey,
elevation: 0,
text: fillButtonText,
state: snapshot.data is ResultLoading<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class GradientFillButton extends StatelessWidget {
this.colorStyle,
this.textStyle,
this.areIconsClose = false,
this.buttonKey,
super.key,
});

Expand Down Expand Up @@ -61,6 +62,8 @@ class GradientFillButton extends StatelessWidget {
/// The style of the [text]
final TextStyle? textStyle;

final Key? buttonKey;

@override
Widget build(BuildContext context) {
final gradient = activeState()
Expand Down Expand Up @@ -149,6 +152,7 @@ class GradientFillButton extends StatelessWidget {
Color primary,
) {
return ElevatedButton(
key: buttonKey,
style: ElevatedButton.styleFrom(
backgroundColor: primary,
padding: padding ?? EdgeInsets.zero,
Expand Down