-
-
Save pilot34/09d692f74d4052670f3bae77dd745889 to your computer and use it in GitHub Desktop.
| // | |
| // UITestCase.swift | |
| // | |
| // Created by Gleb Tarasov on 23/04/2018. | |
| // | |
| import Foundation | |
| import XCTest | |
| import UIKit | |
| class UITestCase: XCTestCase { | |
| private func wait(forElement element: XCUIElement, exists: Bool, timeout: TimeInterval) { | |
| let predicate = NSPredicate(format: "exists == %@", NSNumber(value: exists)) | |
| let e = XCTNSPredicateExpectation(predicate: predicate, object: element) | |
| let result = XCTWaiter().wait(for: [ e ], timeout: timeout) | |
| XCTAssert(result == .completed) | |
| } | |
| func wait(forWebViewElement element: XCUIElementTypeQueryProvider, timeout: TimeInterval = 20) { | |
| // xcode has bug, so we cannot directly access webViews XCUIElements | |
| // as a workaround we can check debugDesciption and parse it, that works | |
| let predicate = NSPredicate { obj, _ in | |
| guard let el = obj as? XCUIElement else { | |
| return false | |
| } | |
| // If element has firstMatch, than there will be description of that at the end | |
| // If no match - it will be ended with "FirstMatch\n" | |
| return !el.firstMatch.debugDescription.hasSuffix("First Match\n") | |
| } | |
| // we need to take .firstMatch, because we parse description for that | |
| let e = XCTNSPredicateExpectation(predicate: predicate, object: element.firstMatch) | |
| let result = XCTWaiter().wait(for: [ e ], timeout: timeout) | |
| XCTAssert(result == .completed) | |
| } | |
| func wait(forElement element: XCUIElement, timeout: TimeInterval = 20) { | |
| wait(forElement: element, exists: true, timeout: timeout) | |
| } | |
| func wait(elementToHide element: XCUIElement, timeout: TimeInterval = 20) { | |
| wait(forElement: element, exists: false, timeout: timeout) | |
| } | |
| func wait(seconds: TimeInterval) { | |
| Thread.sleep(forTimeInterval: seconds) | |
| } | |
| private func coordinate(forWebViewElement element: XCUIElement) -> XCUICoordinate? { | |
| // parse description to find its frame | |
| let descr = element.firstMatch.debugDescription | |
| guard let rangeOpen = descr.range(of: "{{", options: [.backwards]), | |
| let rangeClose = descr.range(of: "}}", options: [.backwards]) else { | |
| return nil | |
| } | |
| let frameStr = String(descr[rangeOpen.lowerBound..<rangeClose.upperBound]) | |
| let rect = CGRectFromString(frameStr) | |
| // tap on the center | |
| let center = CGVector(dx: rect.midX, dy: rect.midY) | |
| let coordinate = XCUIApplication().coordinate(withNormalizedOffset: .zero).withOffset(center) | |
| return coordinate | |
| } | |
| func tap(onWebViewElement element: XCUIElement) { | |
| // xcode has bug, so we cannot directly access webViews XCUIElements | |
| // as workaround we can check debugDesciption, find frame and tap by coordinate | |
| // wait for element to appear before tap | |
| wait(forWebViewElement: element) | |
| let coord = coordinate(forWebViewElement: element) | |
| coord?.tap() | |
| } | |
| func exists(webViewElement element: XCUIElement) -> Bool { | |
| return coordinate(forWebViewElement: element) != nil | |
| } | |
| func typeText(_ text: String, toWebViewField element: XCUIElement) { | |
| // xcode has bug, so we cannot directly access webViews XCUIElements | |
| // as workaround we can check debugDesciption, find frame, tap by coordinate, | |
| // and then paste text there | |
| // wait for element to appear before tap | |
| wait(forWebViewElement: element) | |
| guard let coordBeforeTap = coordinate(forWebViewElement: element) else { | |
| XCTFail("no element \(element)") | |
| return | |
| } | |
| // "typeText" doesn't work, so we paste text | |
| // first tap to activate field | |
| UIPasteboard.general.string = text | |
| coordBeforeTap.tap() | |
| // wait for keyboard to appear | |
| wait(forWebViewElement: XCUIApplication().keyboards.firstMatch) | |
| // after tap coordinate can change | |
| guard let coordAfterTap = coordinate(forWebViewElement: element) else { | |
| XCTFail("no element \(element)") | |
| return | |
| } | |
| // tap one more time for "paste" menu | |
| coordAfterTap.press(forDuration: 1) | |
| wait(forElement: XCUIApplication().menuItems["Paste"]) | |
| if XCUIApplication().menuItems["Select All"].exists { | |
| // if there was a text - remove it, by pressing Select All and Cut | |
| XCUIApplication().menuItems["Select All"].tap() | |
| XCUIApplication().menuItems["Cut"].tap() | |
| // close keyboard | |
| XCUIApplication().toolbars.buttons["Done"].tap() | |
| // call this method one more time | |
| typeText(text, toWebViewField: element) | |
| return | |
| } | |
| XCUIApplication().menuItems["Paste"].tap() | |
| // close keyboard | |
| XCUIApplication().toolbars.buttons["Done"].tap() | |
| } | |
| } |
@seyitcodeit
check disclaimer for this article: https://medium.com/@pilot34/work-with-sfsafariviewcontroller-or-wkwebview-in-xcode-ui-tests-8b14fd281a1f
we don't need this code now, there is an easier workaround
Thank you for your quick reply and article actually( this is the one of a few articles about this issue) i am trying to reference this article but i am little confused. Should i use this functions to test my webview login screen ? Since i have just coordinates from debugdescription of my textfields no placeholders.
let textfield = app.textFields["username"]
typeText("[email protected]", toWebViewField: textfield)
it fails because dont find username so only solution remains coordinate ? Thank you
I'm not in the context, this was 2 years ago. But as I remember, you should be able to enter the text into the text field inside WKWebView just with XCUIElement default method:
app.textFields["username"].typeText("[email protected]")So the hack in this Gist is not needed at all. But maybe I'm wrong.
Hi Gleb i am working for same issue. Could you please share with me a sample test class for use these function to test ?