From fa2ffbd4471ef943a22f58693cb2c19b6ca53318 Mon Sep 17 00:00:00 2001 From: kenji Date: Fri, 4 Jan 2019 13:25:00 +0100 Subject: [PATCH] Fix issue when tapping accessibility element's view not available yet --- CHANGELOG.md | 15 ++++- STKTestAppTests/STKSoloTests.swift | 15 +++++ SwiftiumTestingKit/Info.plist | 2 +- SwiftiumTestingKit/STKSolo.swift | 88 ++++++++++++++++++------------ 4 files changed, 80 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3038e..5c504e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,16 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Removed -## [0.1.7](https://github.com/openium/SwiftiumTestingKit/compare/latest...HEAD) + +## [0.1.8](https://github.com/openium/SwiftiumTestingKit/compare/v0.1.8...HEAD) +### Added + +### Changed +Fix issue when tapping accessibility element's view not available yet + +### Removed + +## [0.1.7](https://github.com/openium/SwiftiumTestingKit/compare/v0.1.7...HEAD) ### Added Allow pilotable server to specify responses headers for all requests and responses headers for each request @@ -18,7 +27,7 @@ Allow pilotable server to specify responses headers for all requests and respons ### Removed -## [0.1.6](https://github.com/openium/SwiftiumTestingKit/compare/latest...HEAD) +## [0.1.6](https://github.com/openium/SwiftiumTestingKit/compare/v0.1.6...HEAD) ### Added ### Changed @@ -26,7 +35,7 @@ Fixed search for text containing \n ### Removed -## [0.1.5](https://github.com/openium/SwiftiumTestingKit/compare/latest...HEAD) +## [0.1.5](https://github.com/openium/SwiftiumTestingKit/compare/v0.1.5...HEAD) ### Added ### Changed diff --git a/STKTestAppTests/STKSoloTests.swift b/STKTestAppTests/STKSoloTests.swift index b8792e8..a21944c 100644 --- a/STKTestAppTests/STKSoloTests.swift +++ b/STKTestAppTests/STKSoloTests.swift @@ -174,4 +174,19 @@ class STKSoloTests: XCTestCase { XCTAssertTrue(waitForCell) XCTAssertTrue(waitForDeleteConfirmationButton) } + + func testWaitForText_onAlertViewController_shouldFindOKButton() { + // Given + sut.showViewControllerInCleanWindow(viewController) + let alertVC = UIAlertController(title: "title", message: "message", preferredStyle: .alert) + let alertOKAction = UIAlertAction(title: "OK", style: .default, handler: nil) + alertVC.addAction(alertOKAction) + viewController.present(alertVC, animated: false, completion: nil) + + // When + let waitForOK = sut.waitFor(tappableText: "OK", andTapIt: true) + + // Expect + XCTAssertTrue(waitForOK) + } } diff --git a/SwiftiumTestingKit/Info.plist b/SwiftiumTestingKit/Info.plist index a763074..953587a 100644 --- a/SwiftiumTestingKit/Info.plist +++ b/SwiftiumTestingKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1.7 + 0.1.8 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/SwiftiumTestingKit/STKSolo.swift b/SwiftiumTestingKit/STKSolo.swift index c615375..73cfaff 100644 --- a/SwiftiumTestingKit/STKSolo.swift +++ b/SwiftiumTestingKit/STKSolo.swift @@ -14,6 +14,10 @@ import SimulatorStatusMagiciOS public class STKSolo: NSObject { public var animationSpeed: Float = 1.0 public var timeToWaitForever: Double = 20 + static var isSingleTestRunning: Bool = { + return isSingleTestRun() + }() + var internalTimeToWaitForever: Double { var time = timeToWaitForever if isUserJenkinsOrHudson() { @@ -83,6 +87,15 @@ public class STKSolo: NSObject { self.window = nil } + func waitRunLoops(timeBetweenChecks: TimeInterval) { + RunLoop.current.run(mode: .default, before: Date(timeIntervalSinceNow: timeBetweenChecks / 3.0)) + RunLoop.current.run(mode: .common, before: Date(timeIntervalSinceNow: timeBetweenChecks / 3.0)) + RunLoop.current.run(mode: .tracking, before: Date(timeIntervalSinceNow: timeBetweenChecks / 3.0)) + if !STKSolo.isSingleTestRunning { + CATransaction.flush() // prevents animations + } + } + func waitClosureToReturnTrue(_ closure: () -> Bool, withinTimeout timeout: TimeInterval, timeBetweenChecks: TimeInterval) { @@ -92,7 +105,7 @@ public class STKSolo: NSObject { if closure() { break } - RunLoop.current.run(mode: .default, before: Date(timeIntervalSinceNow: timeBetweenChecks)) + waitRunLoops(timeBetweenChecks: timeBetweenChecks) now = Date.timeIntervalSinceReferenceDate } while (now - start) < timeout } @@ -136,8 +149,14 @@ public class STKSolo: NSObject { let cleanedText = accessibilityCleaned(text: tappableText) let element = waitForAccessibilityElement { $0.accessibilityLabel == cleanedText } if let element = element { - if let view = try? UIAccessibilityElement.viewContaining(element, tappable: true) { + var view: UIView? = nil + waitClosureToReturnTrue({ () -> Bool in + view = try? UIAccessibilityElement.viewContaining(element, tappable: true) + return view != nil + }, withinTimeout: timeoutForWaitForMethods, timeBetweenChecks: timeBetweenChecks) + if let view = view { testActor.tap(element, in: view) + waitRunLoops(timeBetweenChecks: timeBetweenChecks) } else { return false } @@ -216,45 +235,42 @@ public class STKSolo: NSObject { #endif } -extension STKSolo { - - func isUserJenkinsOrHudson() -> Bool { - var username = NSUserName() - #if targetEnvironment(simulator) - if username.isEmpty { - let bundlePathComponents = (Bundle.main.bundlePath as NSString).pathComponents - if bundlePathComponents.count >= 3 && bundlePathComponents[0] == "/" && bundlePathComponents[1] == "Users" { - username = bundlePathComponents[2] - } - } - #endif - return username == "hudson" || username == "jenkins" - } - - func isSingleTestRun() -> Bool { - /* - XCTestConfiguration *testConfiguration = [XCTestConfiguration activeTestConfiguration]; - // KO when running test of only one class (testsToRun is an Array with class/test names, or just class name) - return [testConfiguration.testsToRun count] == 1 && [[testConfiguration.testsToRun anyObject] containsString:@"/"]; - */ - if let plistPath = ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"], - let plistBinary = FileManager.default.contents(atPath: plistPath), - let unarchivedObject = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(plistBinary), - let object = unarchivedObject as? NSObject, - let testsToRun = object.value(forKey: "testsToRun") as? NSSet, - testsToRun.count == 1, - let singleTestToRun = testsToRun.anyObject() as? NSString { - return singleTestToRun.contains("/") - } else { - return false +func isUserJenkinsOrHudson() -> Bool { + var username = NSUserName() + #if targetEnvironment(simulator) + if username.isEmpty { + let bundlePathComponents = (Bundle.main.bundlePath as NSString).pathComponents + if bundlePathComponents.count >= 3 && bundlePathComponents[0] == "/" && bundlePathComponents[1] == "Users" { + username = bundlePathComponents[2] } } - - func isMultipleTestsRun() -> Bool { - return XCTestSuite.default.testCaseCount > 1 + #endif + return username == "hudson" || username == "jenkins" +} + +func isSingleTestRun() -> Bool { + /* + XCTestConfiguration *testConfiguration = [XCTestConfiguration activeTestConfiguration]; + // KO when running test of only one class (testsToRun is an Array with class/test names, or just class name) + return [testConfiguration.testsToRun count] == 1 && [[testConfiguration.testsToRun anyObject] containsString:@"/"]; + */ + if let plistPath = ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"], + let plistBinary = FileManager.default.contents(atPath: plistPath), + let unarchivedObject = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(plistBinary), + let object = unarchivedObject as? NSObject, + let testsToRun = object.value(forKey: "testsToRun") as? NSSet, + testsToRun.count == 1, + let singleTestToRun = testsToRun.anyObject() as? NSString { + return singleTestToRun.contains("/") + } else { + return false } } +func isMultipleTestsRun() -> Bool { + return XCTestSuite.default.testCaseCount > 1 +} + extension STKSolo: KIFTestActorDelegate { public func fail(with exception: NSException!, stopTest stop: Bool) { lastExceptions.append(exception)