Skip to content

Commit

Permalink
Revert "Add support for Selector.instance on iOS (#952)"
Browse files Browse the repository at this point in the history
This reverts commit 9db9e4d.
  • Loading branch information
bartekpacia committed Mar 5, 2023
1 parent 30033f7 commit 2318876
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 218 deletions.
1 change: 1 addition & 0 deletions .github/workflows/patrol-prepare.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ jobs:
fail-fast: false
matrix:
include:
- version: '3.3.0'
- channel: stable

defaults:
Expand Down
25 changes: 5 additions & 20 deletions .github/workflows/test-ios-simulator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ jobs:
- name: Set simulator location
run: xcrun simctl location booted set 52.17469,21.03193

- run: patrol test -t integration_test/smokes/graceful_fail_test.dart
if: success() || failure()

- run: patrol test -t integration_test/example_test.dart
if: success() || failure()

Expand All @@ -65,7 +62,7 @@ jobs:
if: success() || failure()

- run: patrol test -t integration_test/open_quick_settings_test.dart
if: success() || failure() # not on simulator
if: success() || failure() # should work, but is empty on Simulator

- run: patrol test -t integration_test/permissions_location_test.dart
if: success() || failure()
Expand All @@ -74,28 +71,16 @@ jobs:
if: success() || failure()

- run: patrol test -t integration_test/service_cellular_test.dart
if: ${{ false }} # not on simulator
if: ${{ false }} # Not on Simulator

- run: patrol test -t integration_test/service_dark_mode_test.dart
if: success() || failure()

- run: patrol test -t integration_test/service_wifi_test.dart
if: ${{ false }} # not on simulator

- run: patrol test -t integration_test/swipe_test.dart
if: ${{ false }} # not on iOS

- run: patrol test -t integration_test/webview_hackernews_test.dart
if: success() || failure()
if: ${{ false }} # Not on Simulator

- run: patrol test -t integration_test/webview_leancode_test.dart
if: ${{ false }} # temporarily broken

- run: patrol test -t integration_test/webview_login_test.dart
if: success() || failure()

- run: patrol test -t integration_test/webview_stackoverflow_test.dart
if: success() || failure()
- run: patrol test -t integration_test/webview_all_test.dart
if: ${{ false }} # Temporarily broken

- name: Set job status
id: set_status
Expand Down
6 changes: 0 additions & 6 deletions packages/patrol/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
## Unreleased

- Make `Selector.instance` field have effect when running tests on iOS (#952).

Previously it had effect only when running tests on Android.

## 1.0.5+1

- Update small typo in pub.dev listing (#1034)
Expand Down
12 changes: 4 additions & 8 deletions packages/patrol/example/integration_test/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ export 'package:example/main.dart';
export 'package:flutter_test/flutter_test.dart';
export 'package:patrol/patrol.dart';

final globalPatrolTesterConfig = PatrolTesterConfig();
final globalNativeAutomatorConfig = NativeAutomatorConfig(
findTimeout: Duration(seconds: 30), // Simulator on GitHub Actions is so slow
);
final _patrolTesterConfig = PatrolTesterConfig();
final _nativeAutomatorConfig = NativeAutomatorConfig();

Future<void> createApp(PatrolTester $) async {
await setUpTimezone();
Expand All @@ -19,14 +17,12 @@ Future<void> createApp(PatrolTester $) async {
void patrol(
String description,
Future<void> Function(PatrolTester) callback, {
PatrolTesterConfig? patrolTesterConfig,
NativeAutomatorConfig? nativeAutomatorConfig,
bool? skip,
}) {
patrolTest(
description,
config: patrolTesterConfig ?? globalPatrolTesterConfig,
nativeAutomatorConfig: nativeAutomatorConfig ?? globalNativeAutomatorConfig,
config: _patrolTesterConfig,
nativeAutomatorConfig: _nativeAutomatorConfig,
nativeAutomation: true,
skip: skip,
callback,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ void main() {
patrol('interacts with the orange website in a webview', ($) async {
await createApp($);

await $('Open webview (Hacker News)').scrollTo().tap();
await $('Open webview (Hacker News').scrollTo().tap();

await $.native.tap(Selector(text: 'login'));
await $.native.enterTextByIndex('[email protected]', index: 0);
await $.native.enterTextByIndex('[email protected]', index: 0);
await $.native.enterTextByIndex('ny4ncat', index: 1);
await $.native.tap(Selector(text: 'login'));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@ import 'common.dart';
void main() async {
patrol('interacts with the LeanCode website in a webview', ($) async {
await createApp($);

await $('Open webview (LeanCode)').scrollTo().tap();
await $.native.tap(Selector(text: 'Accept cookies'));

// open dropdown
await $.native.tap(Selector(text: 'Accept cookies'));
await $.native.tap(Selector(text: 'What do you do in IT?', instance: 1));

// select option in dropdown
await $.native.tap(Selector(text: 'Developer'));

// close dropdown
await $.native.tap(Selector(text: 'What do you do in IT?', instance: 1));

await $.native.tap(Selector(text: '1 item selected'));
await $.native.enterTextByIndex('[email protected]', index: 0);
});
}
131 changes: 41 additions & 90 deletions packages/patrol/ios/Classes/AutomatorServer/Automator.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import XCTest

let MICROSECONDS_IN_SECOND: UInt32 = 1_000_000

class Automator {
private lazy var device: XCUIDevice = {
return XCUIDevice.shared
Expand Down Expand Up @@ -56,70 +54,57 @@ class Automator {

// MARK: General UI interaction

func tap(onText text: String, inApp bundleId: String, atIndex index: Int) async throws {
let view = "view with text \(format: text) in app \(bundleId) at index \(index)"

try await runAction("tapping on \(view)") {
func tap(on text: String, inApp bundleId: String) async throws {
try await runAction("tapping on view with text \(format: text) in app \(bundleId)") {
let app = try self.getApp(withBundleId: bundleId)
let elementQuery = app.descendants(matching: .any).matching(identifier: text)
let element = app.descendants(matching: .any)[text]

Logger.shared.i("waiting for existence of \(view)")
guard let element = self.waitForView(query: elementQuery, index: index) else {
throw PatrolError.viewNotExists(view)
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")

Logger.shared.i("found \(view), will tap on it")
element.tap()
element.firstMatch.forceTap()
}
}

func doubleTap(onText text: String, inApp bundleId: String, atIndex index: Int) async throws {
let view = "view with text \(format: text) in app \(bundleId) at index \(index)"

try await runAction("double tapping on \(view)") {
func doubleTap(on text: String, inApp bundleId: String) async throws {
try await runAction("double tapping on text \(format: text) in app \(bundleId)") {
let app = try self.getApp(withBundleId: bundleId)
let elementQuery = app.descendants(matching: .any).matching(identifier: text)
let element = app.descendants(matching: .any)[text]

Logger.shared.i("waiting for existence of \(view)")
guard let element = self.waitForView(query: elementQuery, index: index) else {
throw PatrolError.viewNotExists(view)
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), will double tap on it")
element.doubleTap()
element.firstMatch.forceTap()
}
}

func enterText(_ data: String, byText text: String, inApp bundleId: String, atIndex index: Int)
async throws
{
let view = "text field with ident/label \(format: text) in app \(bundleId) at index \(index)"

try await runAction("entering text \(format: data) into \(view)") {
func enterText(_ data: String, by text: String, inApp bundleId: String) async throws {
try await runAction(
"entering text \(format: data) into text field with text \(text) in app \(bundleId)"
) {
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 elementQuery = app.descendants(matching: .any).matching(predicate).matching(
identifier: text)
guard let element = self.waitForView(query: elementQuery, index: index) else {
throw PatrolError.viewNotExists(view)
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)")
}

element.tap()
element.typeText(data)
element.firstMatch.typeText(data)
}
}

func enterText(_ data: String, byIndex index: Int, inApp bundleId: String) async throws {
func enterText(_ data: String, by index: Int, inApp bundleId: String) async throws {
try await runAction("entering text \(format: data) by index \(index) in app \(bundleId)") {
let app = try self.getApp(withBundleId: bundleId)

Expand All @@ -133,12 +118,18 @@ class Automator {
orPredicateWithSubpredicates: [textFieldPredicate, secureTextFieldPredicate]
)

let elementQuery = app.descendants(matching: .any).matching(predicate)
guard let element = self.waitForView(query: elementQuery, index: index) else {
let textFieldsQuery = app.descendants(matching: .any).matching(predicate)
guard
let element = self.waitFor(
query: textFieldsQuery,
byIndex: index,
timeout: self.timeout
)
else {
throw PatrolError.viewNotExists("text field at index \(index) in app \(bundleId)")
}

element.tap()
element.forceTap()
element.typeText(data)
}
}
Expand Down Expand Up @@ -515,8 +506,7 @@ class Automator {
}
}

// MARK: Private common methods

// MARK: Private stuff
private func isSimulator() -> Bool {
#if targetEnvironment(simulator)
return true
Expand Down Expand Up @@ -632,49 +622,10 @@ class Automator {
Logger.shared.i("\(log)...")
let result = try block()
Logger.shared.i("done \(log)")
Logger.shared.i("result: \(result), type: \(type(of: result))")
Logger.shared.i("result: \(result)")
return result
}
}

// MARK: Custom view utilities

/// Adapted from https://stackoverflow.com/q/47880395/7009800
@discardableResult
func waitForAnyElement(elements: [XCUIElement], timeout: TimeInterval? = nil) -> XCUIElement? {
var foundElement: XCUIElement?
let startTime = Date()

while Date().timeIntervalSince(startTime) < (timeout ?? self.timeout) {
if let elementFound = elements.first(where: { $0.exists }) {
foundElement = elementFound
break
}
usleep(MICROSECONDS_IN_SECOND * 1)
}

return foundElement
}

/// Adapted from https://stackoverflow.com/q/47880395/7009800
@discardableResult
func waitForView(query: XCUIElementQuery, index: Int, timeout: TimeInterval? = nil)
-> XCUIElement?
{
var foundElement: XCUIElement?
let startTime = Date()

while Date().timeIntervalSince(startTime) < (timeout ?? self.timeout) {
let elements = query.allElementsBoundByIndex
if index < elements.count && elements[index].exists {
foundElement = elements[index]
break
}
usleep(MICROSECONDS_IN_SECOND * 1)
}

return foundElement
}
}

// MARK: Utilities
Expand Down
Loading

0 comments on commit 2318876

Please sign in to comment.