From df642eed36001739bb182b755fbafefbf0ca39a2 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 12:29:05 +0200 Subject: [PATCH 1/9] add second webview screen displaying hackernews.com --- ...{webview_test.dart => webview_a_test.dart} | 6 ++--- packages/patrol/example/ios/Podfile.lock | 2 +- packages/patrol/example/lib/main.dart | 13 ++++++++-- ...view_screen.dart => webview_screen_a.dart} | 6 ++--- .../patrol/example/lib/webview_screen_b.dart | 26 +++++++++++++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) rename packages/patrol/example/integration_test/{webview_test.dart => webview_a_test.dart} (94%) rename packages/patrol/example/lib/{webview_screen.dart => webview_screen_a.dart} (84%) create mode 100644 packages/patrol/example/lib/webview_screen_b.dart diff --git a/packages/patrol/example/integration_test/webview_test.dart b/packages/patrol/example/integration_test/webview_a_test.dart similarity index 94% rename from packages/patrol/example/integration_test/webview_test.dart rename to packages/patrol/example/integration_test/webview_a_test.dart index 2c96e9fe5..1be344938 100644 --- a/packages/patrol/example/integration_test/webview_test.dart +++ b/packages/patrol/example/integration_test/webview_a_test.dart @@ -34,7 +34,7 @@ Future main() async { extension PatrolX on NativeAutomator { Future waitAndTap(PatrolTester $, Selector selector) async { await tap(selector, appId: resolvedAppId); - await $.pumpAndSettle(); + //await $.pumpAndSettle(); } Future waitAndEnterText( @@ -43,7 +43,7 @@ extension PatrolX on NativeAutomator { required String text, }) async { await enterText(selector, text: text, appId: resolvedAppId); - await $.pumpAndSettle(); + //await $.pumpAndSettle(); } Future waitAndEnterTextByIndex( @@ -53,6 +53,6 @@ extension PatrolX on NativeAutomator { required int index, }) async { await enterTextByIndex(text, index: index, appId: resolvedAppId); - await $.pumpAndSettle(); + //await $.pumpAndSettle(); } } diff --git a/packages/patrol/example/ios/Podfile.lock b/packages/patrol/example/ios/Podfile.lock index 4ba492ba9..d27ab81dc 100644 --- a/packages/patrol/example/ios/Podfile.lock +++ b/packages/patrol/example/ios/Podfile.lock @@ -34,7 +34,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401 integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 diff --git a/packages/patrol/example/lib/main.dart b/packages/patrol/example/lib/main.dart index 95a55411a..b01c5fb02 100644 --- a/packages/patrol/example/lib/main.dart +++ b/packages/patrol/example/lib/main.dart @@ -4,7 +4,8 @@ import 'package:example/notifications_screen.dart'; import 'package:example/overlay_screen.dart'; import 'package:example/permissions_screen.dart'; import 'package:example/scrolling_screen.dart'; -import 'package:example/webview_screen.dart'; +import 'package:example/webview_screen_a.dart'; +import 'package:example/webview_screen_b.dart'; import 'package:flutter/material.dart'; void main() { @@ -180,11 +181,19 @@ class _ExampleHomePageState extends State { TextButton( onPressed: () async => Navigator.of(context).push( MaterialPageRoute( - builder: (_) => const WebViewScreen(), + builder: (_) => const WebViewScreenA(), ), ), child: const Text('Open webview screen'), ), + TextButton( + onPressed: () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const WebViewScreenB(), + ), + ), + child: const Text('Open webview screen B'), + ), ], ), floatingActionButton: FloatingActionButton( diff --git a/packages/patrol/example/lib/webview_screen.dart b/packages/patrol/example/lib/webview_screen_a.dart similarity index 84% rename from packages/patrol/example/lib/webview_screen.dart rename to packages/patrol/example/lib/webview_screen_a.dart index 5d2fa6e5f..371a67848 100644 --- a/packages/patrol/example/lib/webview_screen.dart +++ b/packages/patrol/example/lib/webview_screen_a.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; -class WebViewScreen extends StatelessWidget { - const WebViewScreen({super.key}); +class WebViewScreenA extends StatelessWidget { + const WebViewScreenA({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('WebView'), + title: const Text('WebView A'), ), body: WebView( debuggingEnabled: true, diff --git a/packages/patrol/example/lib/webview_screen_b.dart b/packages/patrol/example/lib/webview_screen_b.dart new file mode 100644 index 000000000..d69507f1f --- /dev/null +++ b/packages/patrol/example/lib/webview_screen_b.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class WebViewScreenB extends StatelessWidget { + const WebViewScreenB({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('WebView B'), + ), + body: WebView( + debuggingEnabled: true, + initialUrl: 'https://hackernews.com', + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (controller) => print('WebView created'), + onPageStarted: (url) => print('Page started loading: $url'), + onProgress: (progress) { + print('WebView is loading (progress : $progress%)'); + }, + onPageFinished: (url) => print('Page finished loading: $url'), + ), + ); + } +} From 8548c68934420a0d97c11dbf624e2d63d2cfe714 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 12:53:56 +0200 Subject: [PATCH 2/9] fix strange bug with `tap()` on iOS --- .../AutomatorServerUITests/Automator.swift | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift index d2d25beed..2b741b078 100644 --- a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift +++ b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift @@ -55,16 +55,18 @@ class Automator { // MARK: General UI interaction func tap(on text: String, inApp bundleId: String) async throws { - try await runAction("tapping on text \(text)") { + try await runAction("tapping on view with text \(format: text)") { let app = try self.getApp(withBundleId: bundleId) let element = app.descendants(matching: .any)[text] + Logger.shared.i("waiting for existence of view with text \(format: text)") let exists = element.waitForExistence(timeout: self.timeout) guard exists else { throw PatrolError.viewNotExists("view with text \(format: text) in app \(format: bundleId)") } + Logger.shared.i("found view with text \(format: text), will tap on it") - element.firstMatch.tap() + element.firstMatch.forceTap() } } @@ -78,7 +80,7 @@ class Automator { throw PatrolError.viewNotExists("view with text \(format: text) in app \(format: bundleId)") } - element.firstMatch.tap() + element.firstMatch.forceTap() } } @@ -106,7 +108,7 @@ class Automator { throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") } - element.firstMatch.tap() + element.firstMatch.forceTap() element.firstMatch.typeText(data) } } @@ -508,3 +510,15 @@ extension String.StringInterpolation { appendInterpolation("\"\(value)\"") } } + +// Adapted from https://samwize.com/2016/02/28/everything-about-xcode-ui-testing-snapshot/ +extension XCUIElement { + func forceTap() { + if self.isHittable { + self.tap() + } else { + let coordinate = self.coordinate(withNormalizedOffset: CGVectorMake(0.0, 0.0)) + coordinate.tap() + } + } +} From 289679851f896a974f0b59866745ecd8e155a523 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 13:18:11 +0200 Subject: [PATCH 3/9] divide webview tests into a (leancode) and b (hackernews) --- .../integration_test/webview_a_test.dart | 4 +- .../integration_test/webview_b_test.dart | 58 +++++++++++++++++++ packages/patrol/example/lib/main.dart | 2 +- 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 packages/patrol/example/integration_test/webview_b_test.dart diff --git a/packages/patrol/example/integration_test/webview_a_test.dart b/packages/patrol/example/integration_test/webview_a_test.dart index 1be344938..479193bc1 100644 --- a/packages/patrol/example/integration_test/webview_a_test.dart +++ b/packages/patrol/example/integration_test/webview_a_test.dart @@ -11,9 +11,9 @@ Future main() async { ($) async { await $.pumpWidgetAndSettle(ExampleApp()); - await $('Open webview screen').scrollTo(); + await $('Open webview screen A').scrollTo(); - await $.native.tap(Selector(text: 'Open webview screen')); + await $.native.tap(Selector(text: 'Open webview screen A')); await $.pumpAndSettle(); diff --git a/packages/patrol/example/integration_test/webview_b_test.dart b/packages/patrol/example/integration_test/webview_b_test.dart new file mode 100644 index 000000000..b31a31a78 --- /dev/null +++ b/packages/patrol/example/integration_test/webview_b_test.dart @@ -0,0 +1,58 @@ +import 'package:example/main.dart'; +import 'package:patrol/patrol.dart'; + +import 'config.dart'; + +Future main() async { + patrolTest( + 'navigates through the app using only native semantics', + config: patrolConfig, + nativeAutomation: true, + ($) async { + await $.pumpWidgetAndSettle(ExampleApp()); + + await $('Open webview screen B').scrollTo(); + + await $.native.tap(Selector(text: 'Open webview screen B')); + + await $.pumpAndSettle(); + + await $.native.waitAndTap($, Selector(text: 'login')); + await $.native.waitAndEnterTextByIndex( + $, + text: 'test@leancode.pl', + index: 0, + ); + await $.native.waitAndEnterTextByIndex( + $, + text: 'ny4ncat', + index: 1, + ); + }, + ); +} + +extension PatrolX on NativeAutomator { + Future waitAndTap(PatrolTester $, Selector selector) async { + await tap(selector, appId: resolvedAppId); + //await $.pumpAndSettle(); + } + + Future waitAndEnterText( + PatrolTester $, + Selector selector, { + required String text, + }) async { + await enterText(selector, text: text, appId: resolvedAppId); + //await $.pumpAndSettle(); + } + + Future waitAndEnterTextByIndex( + PatrolTester $, { + required String text, + required int index, + }) async { + await enterTextByIndex(text, index: index, appId: resolvedAppId); + //await $.pumpAndSettle(); + } +} diff --git a/packages/patrol/example/lib/main.dart b/packages/patrol/example/lib/main.dart index b01c5fb02..8146e9d47 100644 --- a/packages/patrol/example/lib/main.dart +++ b/packages/patrol/example/lib/main.dart @@ -184,7 +184,7 @@ class _ExampleHomePageState extends State { builder: (_) => const WebViewScreenA(), ), ), - child: const Text('Open webview screen'), + child: const Text('Open webview screen A'), ), TextButton( onPressed: () async => Navigator.of(context).push( From f47a5f3f5797fc9dc2568c510504073785b4932e Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 14:03:05 +0200 Subject: [PATCH 4/9] clean up webview tests, make them work reliably on Simulator and iPhone --- .../integration_test/webview_a_test.dart | 49 +++---------------- .../integration_test/webview_b_test.dart | 47 ++---------------- packages/patrol/example/lib/main.dart | 16 +++--- 3 files changed, 20 insertions(+), 92 deletions(-) diff --git a/packages/patrol/example/integration_test/webview_a_test.dart b/packages/patrol/example/integration_test/webview_a_test.dart index 479193bc1..5f733f625 100644 --- a/packages/patrol/example/integration_test/webview_a_test.dart +++ b/packages/patrol/example/integration_test/webview_a_test.dart @@ -5,54 +5,19 @@ import 'config.dart'; Future main() async { patrolTest( - 'navigates through the app using only native semantics', + 'interacts with the LeanCode website in a webview', config: patrolConfig, nativeAutomation: true, ($) async { await $.pumpWidgetAndSettle(ExampleApp()); - await $('Open webview screen A').scrollTo(); + await $('Open webview screen A').scrollTo().tap(); - await $.native.tap(Selector(text: 'Open webview screen A')); - - await $.pumpAndSettle(); - - await $.native.waitAndTap($, Selector(text: 'Accept cookies')); - await $.native.waitAndTap($, Selector(text: 'Select items')); - await $.native.waitAndTap($, Selector(text: 'Developer')); - await $.native.waitAndTap($, Selector(text: '1 item selected')); - await $.native.waitAndEnterTextByIndex( - $, - Selector(text: '1 item selected'), - text: 'test@leancode.pl', - index: 0, - ); + await $.native.tap(Selector(text: 'Accept cookies')); + await $.native.tap(Selector(text: 'Select items')); + await $.native.tap(Selector(text: 'Developer')); + await $.native.tap(Selector(text: '1 item selected')); + await $.native.enterTextByIndex('test@leancode.pl', index: 0); }, ); } - -extension PatrolX on NativeAutomator { - Future waitAndTap(PatrolTester $, Selector selector) async { - await tap(selector, appId: resolvedAppId); - //await $.pumpAndSettle(); - } - - Future waitAndEnterText( - PatrolTester $, - Selector selector, { - required String text, - }) async { - await enterText(selector, text: text, appId: resolvedAppId); - //await $.pumpAndSettle(); - } - - Future waitAndEnterTextByIndex( - PatrolTester $, - Selector selector, { - required String text, - required int index, - }) async { - await enterTextByIndex(text, index: index, appId: resolvedAppId); - //await $.pumpAndSettle(); - } -} diff --git a/packages/patrol/example/integration_test/webview_b_test.dart b/packages/patrol/example/integration_test/webview_b_test.dart index b31a31a78..58176cdd3 100644 --- a/packages/patrol/example/integration_test/webview_b_test.dart +++ b/packages/patrol/example/integration_test/webview_b_test.dart @@ -5,54 +5,17 @@ import 'config.dart'; Future main() async { patrolTest( - 'navigates through the app using only native semantics', + 'interacts with the orange website in a webview', config: patrolConfig, nativeAutomation: true, ($) async { await $.pumpWidgetAndSettle(ExampleApp()); - await $('Open webview screen B').scrollTo(); + await $('Open webview screen B').scrollTo().tap(); - await $.native.tap(Selector(text: 'Open webview screen B')); - - await $.pumpAndSettle(); - - await $.native.waitAndTap($, Selector(text: 'login')); - await $.native.waitAndEnterTextByIndex( - $, - text: 'test@leancode.pl', - index: 0, - ); - await $.native.waitAndEnterTextByIndex( - $, - text: 'ny4ncat', - index: 1, - ); + await $.native.tap(Selector(text: 'login')); + await $.native.enterTextByIndex('test@leancode.pl', index: 0); + await $.native.enterTextByIndex('ny4ncat', index: 1); }, ); } - -extension PatrolX on NativeAutomator { - Future waitAndTap(PatrolTester $, Selector selector) async { - await tap(selector, appId: resolvedAppId); - //await $.pumpAndSettle(); - } - - Future waitAndEnterText( - PatrolTester $, - Selector selector, { - required String text, - }) async { - await enterText(selector, text: text, appId: resolvedAppId); - //await $.pumpAndSettle(); - } - - Future waitAndEnterTextByIndex( - PatrolTester $, { - required String text, - required int index, - }) async { - await enterTextByIndex(text, index: index, appId: resolvedAppId); - //await $.pumpAndSettle(); - } -} diff --git a/packages/patrol/example/lib/main.dart b/packages/patrol/example/lib/main.dart index 8146e9d47..9e62259be 100644 --- a/packages/patrol/example/lib/main.dart +++ b/packages/patrol/example/lib/main.dart @@ -162,14 +162,6 @@ class _ExampleHomePageState extends State { ), child: const Text('Open overlay screen'), ), - TextButton( - onPressed: () async => Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => const PermissionsScreen(), - ), - ), - child: const Text('Open permissions screen'), - ), TextButton( onPressed: () async => Navigator.of(context).push( MaterialPageRoute( @@ -194,6 +186,14 @@ class _ExampleHomePageState extends State { ), child: const Text('Open webview screen B'), ), + TextButton( + onPressed: () async => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const PermissionsScreen(), + ), + ), + child: const Text('Open permissions screen'), + ), ], ), floatingActionButton: FloatingActionButton( From 689e4ffaa905b4372cb0e6e3bf313d4c7430a8fa Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 14:45:40 +0200 Subject: [PATCH 5/9] WIP: supporting SecureTextField on iOS --- .../ios/AutomatorServerUITests/Automator.swift | 18 +++++++++++++++--- packages/patrol/example/ios/Podfile.lock | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift index 2b741b078..08ef82961 100644 --- a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift +++ b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift @@ -89,8 +89,10 @@ class Automator { let app = try self.getApp(withBundleId: bundleId) let element = app.textFields[text] - let exists = element.waitForExistence(timeout: self.timeout) - guard exists else { + guard let element = self.waitForAnyElement( + elements: [app.textFields[text], app.secureTextFields[text]], + timeout: self.timeout + ) else { throw PatrolError.viewNotExists("text field with text \(format: text) in app \(format: bundleId)") } @@ -101,8 +103,18 @@ class Automator { func enterText(_ data: String, by index: Int, inApp bundleId: String) async throws { try await runAction("entering text \(format: data) by index \(index)") { let app = try self.getApp(withBundleId: bundleId) - let element = app.textFields.element(boundBy: index) + +// let textFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeTextField'") +// let secureTextFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeSecureTextField'") +// let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [ +// textFieldPredicate, +// secureTextFieldPredicate +// ] +// ) +// +// let element = app.descendants(matching: .any).matching(textFieldPredicate).element(boundBy: index) + let element = app.textFields.element(boundBy: index) let exists = element.waitForExistence(timeout: self.timeout) guard exists else { throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") diff --git a/packages/patrol/example/ios/Podfile.lock b/packages/patrol/example/ios/Podfile.lock index d27ab81dc..4ba492ba9 100644 --- a/packages/patrol/example/ios/Podfile.lock +++ b/packages/patrol/example/ios/Podfile.lock @@ -34,7 +34,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401 integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 From 4ec986896d1defd6eb3a3b4e63c70d1d523236ca Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Thu, 13 Oct 2022 18:14:27 +0200 Subject: [PATCH 6/9] basic example doesnt work --- .../AutomatorServerUITests/Automator.swift | 44 +++++++++++++------ .../patrol/example/integration_test/logs.txt | 18 ++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 packages/patrol/example/integration_test/logs.txt diff --git a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift index 08ef82961..26eb02c3c 100644 --- a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift +++ b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift @@ -99,31 +99,47 @@ class Automator { element.firstMatch.typeText(data) } } - + func enterText(_ data: String, by index: Int, inApp bundleId: String) async throws { try await runAction("entering text \(format: data) by index \(index)") { let app = try self.getApp(withBundleId: bundleId) -// let textFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeTextField'") -// let secureTextFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeSecureTextField'") -// let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [ -// textFieldPredicate, -// secureTextFieldPredicate -// ] + let textFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeTextField AND exists == 1'") + //let secureTextFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeSecureTextField'") + //let existsPredicate = NSPredicate(format: "exists == 1") +// let predicate = NSCompoundPredicate( +// orPredicateWithSubpredicates: [ +// textFieldPredicate, +// //secureTextFieldPredicate +// ] // ) -// -// let element = app.descendants(matching: .any).matching(textFieldPredicate).element(boundBy: index) - let element = app.textFields.element(boundBy: index) - let exists = element.waitForExistence(timeout: self.timeout) - guard exists else { - throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") - } + + let element = app.descendants(matching: .any).matching(textFieldPredicate) +// let exists = element.waitForExistence(timeout: self.timeout) +// guard exists else { +// throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") +// } element.firstMatch.forceTap() element.firstMatch.typeText(data) } } + +// func enterText(_ data: String, by index: Int, inApp bundleId: String) async throws { +// try await runAction("entering text \(format: data) by index \(index)") { +// let app = try self.getApp(withBundleId: bundleId) +// let element = app.textFields.element(boundBy: index) +// +// let exists = element.waitForExistence(timeout: self.timeout) +// guard exists else { +// throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") +// } +// +// element.firstMatch.tap() +// element.firstMatch.typeText(data) +// } +// } // MARK: Services diff --git a/packages/patrol/example/integration_test/logs.txt b/packages/patrol/example/integration_test/logs.txt new file mode 100644 index 000000000..24397fc15 --- /dev/null +++ b/packages/patrol/example/integration_test/logs.txt @@ -0,0 +1,18 @@ +2022-10-13 17:56:34.463002+0200 AutomatorServerUITests-Runner[11305:1047776] PatrolServer: INFO: entering text "myLogin" by index 0... + t = 1.44s Waiting 10.0s for TextField to exist + t = 2.46s Checking `Expect predicate `exists == 1` for object TextField` + t = 2.46s Checking existence of `TextField` + t = 2.52s Find the TextField + t = 2.57s Tap TextField at {{81.0, 180.0}, {228.0, 33.0}} + t = 2.57s Wait for pl.leancode.patrol.Example to idle + t = 2.59s Find the TextField at {{81.0, 180.0}, {228.0, 33.0}} + t = 2.62s Check for interrupting elements affecting TextField + t = 2.65s Synthesize event + t = 2.95s Wait for pl.leancode.patrol.Example to idle + t = 3.07s Type 'myLogin' into TextField + t = 3.08s Wait for pl.leancode.patrol.Example to idle + t = 3.11s Find the TextField + t = 3.16s Check for interrupting elements affecting TextField + t = 3.19s Synthesize event + t = 3.52s Wait for pl.leancode.patrol.Example to idle +2022-10-13 17:56:36.767774+0200 AutomatorServerUITests-Runner[11305:1047776] PatrolServer: INFO: done entering text "myLogin" by index 0 From 3f5581001d512e653ad4b9e64f77384a3612632e Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Fri, 14 Oct 2022 09:26:52 +0200 Subject: [PATCH 7/9] finally, use predicates correctly --- .../AutomatorServerUITests/Automator.swift | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift index 26eb02c3c..f3cab979b 100644 --- a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift +++ b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift @@ -104,22 +104,16 @@ class Automator { try await runAction("entering text \(format: data) by index \(index)") { let app = try self.getApp(withBundleId: bundleId) + let textFieldPredicate = NSPredicate(format: "elementType == 49") + let secureTextFieldPredicate = NSPredicate(format: "elementType == 50") + let predicate = NSCompoundPredicate( + orPredicateWithSubpredicates: [textFieldPredicate, secureTextFieldPredicate] + ) - let textFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeTextField AND exists == 1'") - //let secureTextFieldPredicate = NSPredicate(format: "elementType == 'XCUIElementTypeSecureTextField'") - //let existsPredicate = NSPredicate(format: "exists == 1") -// let predicate = NSCompoundPredicate( -// orPredicateWithSubpredicates: [ -// textFieldPredicate, -// //secureTextFieldPredicate -// ] -// ) - - let element = app.descendants(matching: .any).matching(textFieldPredicate) -// let exists = element.waitForExistence(timeout: self.timeout) -// guard exists else { -// throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") -// } + let elements = app.descendants(matching: .any).matching(predicate).allElementsBoundByIndex + guard let element = self.waitForAnyElement(elements: elements, timeout: self.timeout) else { + throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") + } element.firstMatch.forceTap() element.firstMatch.typeText(data) From 8d642421a94d5852cb234889ac776e1eb223c037 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Fri, 14 Oct 2022 09:40:43 +0200 Subject: [PATCH 8/9] FIX --- .../AutomatorServerUITests/Automator.swift | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift index f3cab979b..a307a080a 100644 --- a/AutomatorServer/ios/AutomatorServerUITests/Automator.swift +++ b/AutomatorServer/ios/AutomatorServerUITests/Automator.swift @@ -104,36 +104,29 @@ class Automator { try await runAction("entering text \(format: data) by index \(index)") { let app = try self.getApp(withBundleId: bundleId) + // elementType must be specified as integer + // See: + // * https://developer.apple.com/documentation/xctest/xcuielementtype/xcuielementtypetextfield + // * https://developer.apple.com/documentation/xctest/xcuielementtype/xcuielementtypesecuretextfield let textFieldPredicate = NSPredicate(format: "elementType == 49") let secureTextFieldPredicate = NSPredicate(format: "elementType == 50") let predicate = NSCompoundPredicate( orPredicateWithSubpredicates: [textFieldPredicate, secureTextFieldPredicate] ) - let elements = app.descendants(matching: .any).matching(predicate).allElementsBoundByIndex - guard let element = self.waitForAnyElement(elements: elements, timeout: self.timeout) else { - throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") + let textFields = app.descendants(matching: .any).matching(predicate) + let textFieldCount = textFields.allElementsBoundByIndex.count + Logger.shared.i("found \(textFields.count) text fields") + guard index < textFieldCount else { + throw PatrolError.viewNotExists("text field at index \(index)") } - element.firstMatch.forceTap() - element.firstMatch.typeText(data) + + let textField = textFields.element(boundBy: index) + textField.forceTap() + textField.typeText(data) } } - -// func enterText(_ data: String, by index: Int, inApp bundleId: String) async throws { -// try await runAction("entering text \(format: data) by index \(index)") { -// let app = try self.getApp(withBundleId: bundleId) -// let element = app.textFields.element(boundBy: index) -// -// let exists = element.waitForExistence(timeout: self.timeout) -// guard exists else { -// throw PatrolError.viewNotExists("text field at index \(index) in app \(format: bundleId)") -// } -// -// element.firstMatch.tap() -// element.firstMatch.typeText(data) -// } -// } // MARK: Services From d00c7247cc5cfcbcdcfa04ba7c0ca24b15f56e33 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Fri, 14 Oct 2022 09:43:08 +0200 Subject: [PATCH 9/9] remove logs.txt --- .../patrol/example/integration_test/logs.txt | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 packages/patrol/example/integration_test/logs.txt diff --git a/packages/patrol/example/integration_test/logs.txt b/packages/patrol/example/integration_test/logs.txt deleted file mode 100644 index 24397fc15..000000000 --- a/packages/patrol/example/integration_test/logs.txt +++ /dev/null @@ -1,18 +0,0 @@ -2022-10-13 17:56:34.463002+0200 AutomatorServerUITests-Runner[11305:1047776] PatrolServer: INFO: entering text "myLogin" by index 0... - t = 1.44s Waiting 10.0s for TextField to exist - t = 2.46s Checking `Expect predicate `exists == 1` for object TextField` - t = 2.46s Checking existence of `TextField` - t = 2.52s Find the TextField - t = 2.57s Tap TextField at {{81.0, 180.0}, {228.0, 33.0}} - t = 2.57s Wait for pl.leancode.patrol.Example to idle - t = 2.59s Find the TextField at {{81.0, 180.0}, {228.0, 33.0}} - t = 2.62s Check for interrupting elements affecting TextField - t = 2.65s Synthesize event - t = 2.95s Wait for pl.leancode.patrol.Example to idle - t = 3.07s Type 'myLogin' into TextField - t = 3.08s Wait for pl.leancode.patrol.Example to idle - t = 3.11s Find the TextField - t = 3.16s Check for interrupting elements affecting TextField - t = 3.19s Synthesize event - t = 3.52s Wait for pl.leancode.patrol.Example to idle -2022-10-13 17:56:36.767774+0200 AutomatorServerUITests-Runner[11305:1047776] PatrolServer: INFO: done entering text "myLogin" by index 0