Last active
March 26, 2020 21:53
-
-
Save pilot34/09d692f74d4052670f3bae77dd745889 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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() | |
} | |
} |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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