-
-
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 ?