How to capture/extract text value from a text field in EarlGrey? - ios

I'm using EarlGrey framework and I want to select the whole text and capture it from the textfield or search bar. Is there a way to do it?
Thanks in advance!

You extracted the value with XCTest APIs method.
The right way can be found in EarlGrey FAQ
https://github.com/google/EarlGrey/blob/master/docs/faq.md
// Swift
//
// Must use a wrapper class to force pass by reference in Swift 3 closures.
// inout params cannot be modified within closures. http://stackoverflow.com/a/28252105
open class Element {
var text = ""
}
/*
* Example Usage:
*
* let element = Element()
* domainField.performAction(grey_replaceText("hello.there"))
* .performAction(grey_getText(element))
*
* GREYAssertTrue(element.text != "", reason: "get text failed")
*/
public func grey_getText(_ elementCopy: Element) -> GREYActionBlock {
return GREYActionBlock.action(withName: "get text",
constraints: grey_respondsToSelector(#selector(getter: UILabel.text))) { element,
errorOrNil -> Bool in
let elementObject = element as? NSObject
let text = elementObject?.perform(#selector(getter: UILabel.text),
with: nil)?.takeRetainedValue() as? String
elementCopy.text = text ?? ""
return true
}
}

Finally, found out !
Using application.navigationBars["Navigation bar Name"].searchFields["Label of the textField / Search Bar"].value
This fetched the value from the textfield. One thing to be noted is that, the value retrieved will be of the type "any"

Can anyone write the below code with objective-c?
// Swift
//
// Must use a wrapper class to force pass by reference in Swift 3 closures.
// inout params cannot be modified within closures. http://stackoverflow.com/a/28252105
open class Element {
var text = ""
}
/*
* Example Usage:
*
* let element = Element()
* domainField.performAction(grey_replaceText("hello.there"))
* .performAction(grey_getText(element))
*
* GREYAssertTrue(element.text != "", reason: "get text failed")
*/
public func grey_getText(_ elementCopy: Element) -> GREYActionBlock {
return GREYActionBlock.action(withName: "get text",
constraints: grey_respondsToSelector(#selector(getter: UILabel.text))) { element,
errorOrNil -> Bool in
let elementObject = element as? NSObject
let text = elementObject?.perform(#selector(getter: UILabel.text),
with: nil)?.takeRetainedValue() as? String
elementCopy.text = text ?? ""
return true
}
}

Related

How to properly set DataStore for storing boolean value

I want to set a simple switch that'll save a boolean value and if then block in my function.
Currently I have this in my DataStore:
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("userToken")
private val AutoRestartSystemUI = booleanPreferencesKey("AutoRestartSystemUIValue")
}
var getAutoRestartSystemUIValue: Flow<Boolean> = context.dataStore.data.map { preferences ->
(preferences[AutoRestartSystemUI] ?: "") as Boolean
}
suspend fun setAutoRestartSystemUI(value: Boolean) {
context.dataStore.edit { preferences ->
preferences[AutoRestartSystemUI] = value
}
}
}
and
Button(onClick = {
// if [pro.themed.manager.UserStore(context).getAutoRestartSystemUIValue = true] ()
CoroutineScope(Dispatchers.IO).launch {
UserStore(context).setAutoRestartSystemUI(false)
}
}) {
Text(text = UserStore(context).getAutoRestartSystemUIValue.collectAsState(
initial = ""
).toString())
}
in my activity. I have generally no idea of what I should do next and for some weird reason instead of showing value in a text (temp solution for testing) i have
How do i simplify datastore? How do I properly implement switch that'll make it = !it? How to set default value?

How to use 'iterateEnum' function in xcode 10.2.1/swift 4.2 to sort string value from array in specific order as specified in the enum list

As a part of code migration from swift 3.0 xcode 9.4.1 to swift 4.2 xcode 10.2.1, I am facing a issue with array sortting method used in swift 3.0 which work fine in swift 3.x but on xcode 10.2.1/swift 4.2 its doesn't work, return nil value instead of sorted array list:
// Statuses currently come through in an array, which is not sorted in the correct order
// The order is specific, as specified in the enum list
// Create a pre-made list of section headers, only for statuses that have valid activities
Leave is a JSONEncodable modal class and Status is enum string declared within.
Any help would be appreciate. Thanks in advance.
open class Leave: JSONEncodable {
public enum Status: String {
case Drafts = "Drafts"
case PushedBack = "Pushed Back"
case PendingApproval = "Pending Approval"
case UpcomingLeave = "Upcoming Leave"
case History = "History"
case Booked = "Booked"
case Approved = "Approved"
case Denied = "Denied"
case ApprovedAndDeniedRequest = "Approved and Denied Request"
case Error = "Error"
}
}
var orderedSections: [Leave.Status] {
var list: [Leave.Status] = []
for status in iterateEnum(Leave.Status.self) {
list.append(status)
}
return list
}
fileprivate func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
let res: T? = next.hashValue == i ? next : nil
i += 1
return res
}
}
Try following :
var orderedSections: [Leave.Status] {
var list: [Leave.Status] = []
Leave.Status.allCases.forEach { (status) in
list.append(status)
}
// for status in iterateEnum(Leave.Status.self) {
// list.append(status)
// }
return list
}
You don't need iterateEnum function just make your enum conform to CaseIterable protocol after String, to access .allCases.
Example:
public enum Listing: String, CaseIterable {
case a = "a"
case b = "b"
}
Listing.allCases.forEach({print($0)})

rac_textSignal in Swift 3

I am attempting to migrate a Swift 2.2 app that I did not write to Swift 3. All was going well until I ran into some RAC calls. I am not familiar with ReactiveCocoa, which makes this even more challenging.
/**
* Method name: Enable Login Button
* Description: Enable or disable login button based on
* username and password entry
* Parameters: None
*/
func enableLoginButton() -> Void {
self.btnLogin.isEnabled = false
let serviceUrlSignal = self.txtServiceUrl.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let usernameSignal = self.txtFieldUsername.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let userNameCharSignal = usernameSignal.map { $0.characters.count }
let passwordSignal = self.txtFieldPassword.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let passwordCharSignal = passwordSignal.map { $0.characters.count }
userNameCharSignal.combineLatestWith(passwordCharSignal)
.map
{
charArray in charArray.0 > 0 && charArray.1 > 0
}
.observe { event in
if let buttonEnabled = event.value {
self.btnLogin.enabled = buttonEnabled
}
}
viewModel.username <~ usernameSignal
viewModel.password <~ passwordSignal
viewModel.serviceUrl <~ serviceUrlSignal
btnLogin.rac_signalForControlEvents(.TouchUpInside)
.subscribeNext{
button in
print("Authenticate Click!")
}
}
From what I understand, rac_textSignal does not exist in ReactiveSwift. Do I need to install ReactiveObjC or is there a Swift approach that I could use to replace this functionality? Thanks.
You'll need to add ReactiveCocoa in addition to ReactiveSwift.
Since the repo split, the core of what was ReactiveCocoa is now implemented in ReactiveSwift, but ReactiveCocoa adds Cocoa specific things like UIKit bindings which you'll need.
UIKit bindings are accessible via the reactive property, e.g. textfield.reactive.continuousTextValues.
Since these bindings are already correctly typed (as opposed to rac_textSignal), you can replace the whole chunk of
let usernameSignal = self.txtFieldUsername.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
...
viewModel.username <~ usernameSignal
with
viewModel.username <~ self.txtFieldUsername.reactive.continuousTextValues

Cannot call value of non-function type '((AnyClass) -> Bool)!'

here is my code.
let myDeepLinkAction: UAAction = UAAction(block: {(args:UAActionArguments, handler:UAActionCompletionHandler) -> Void in
handler(UAActionResult.empty())
}, acceptingArguments: {(arguments: UAActionArguments) in
if arguments.situation == UASituation.backgroundPush {
return true
}
return ((arguments.value! as AnyObject).isKind(of: NSString) || (arguments.value! as AnyObject).isKind(of: URL))
})
that type error is coming after swift version conversion 2.2 to 3.0,pls give me solution as possible.
I have found the solution,its simple
let myDeepLinkAction: UAAction = UAAction(block: {(args:UAActionArguments, handler:UAActionCompletionHandler) -> Void in
handler(UAActionResult.empty())
}, acceptingArguments: {(arguments: UAActionArguments) in
if arguments.situation == UASituation.backgroundPush {
return true
}
return (arguments.value! is NSString || arguments.value! is URL)
})
return (arguments.value! is NSString || arguments.value! is URL)
Make use of classForKeyedArchiver property
For eg : If you want to find out if a view controller belongs to a certain class , use the following snippet
if sampleController.isKind( of : listOfFlowersViewController.classForKeyedArchiver()!)
{
//your success code here
}
I was having this issue when I updated to swift 3 in xcode 8
for view in subViews {
if ((view as AnyObject).isKind(of : UIScrollView))
{
scrollView = view as? UIScrollView
}
It was showing error "Cannot call value of non-function type '((AnyClass) -> Bool)!"
Then I added this "classForKeyedArchiver()"
for view in subViews {
if ((view as AnyObject).isKind(of :
UIScrollView().classForKeyedArchiver!))
{
scrollView = view as? UIScrollView
}
Thanks a lot,it worked for me.

generic tap function for XCUIApplication

We are trying to migrate from UIAutomation to XCUITests.
For the UIAutomation we came up with a handy 'tapOnName' function which just crawled thru a whole sub element tree and tapped on the element with the first match.
function log(msg) {
UIALogger.logDebug(msg);
}
//recursive function crawling thru an elements hierarchy
//and tapping on the first match of accessibilityIdentifier
//or button text
function tapOnNameWithRoot(name,el) {
if (el.name()==name && el.isVisible()) {
log("tap on itt!!!")
el.tap();
return true;
}
if (el.toString()=="[object UIAButton]" && el.label()==name) {
log("tap on Button!!!")
el.tap();
return true;
}
var elements=el.elements();
if (elements===null || elements===undefined) {
log("elements null or undefined for:"+el.toString());
return false;
}
for(var i=0,len=elements.length ;i<len;i++) {
if (tapOnNameWithRoot(name,elements[i])) {
return true;
}
}
return false;
}
var win = UIATarget.localTarget().frontMostApp().mainWindow();
//for ex taps on a button with the text "pushme" in the
//main UIWindow
tapOnNameWithRoot("pushme",win);
No the question : is it possible to implement the same function using XCUIApplication ?
There is shorthand support for this function in XCTest.
For tapping the first match out of any element, you can get all elements and tap the first one:
let app = XCUIApplication()
let element = app.descendentsMatchingType(.Any)["someIdentifier"]
element.tap()
If you know what type of element it is going to be, it's better to filter by that type first:
let app = XCUIApplication()
let element = app.buttons["someIdentifier"]
element.tap()
Are you looking for something like this:
func tapBasedOnAccessibilityIdentifier(elementType elementType: XCUIElementQuery, accessibilityIdentifier: String) {
var isElementExist = false
for element in elementType.allElementsBoundByIndex {
if element.label == accessibilityIdentifier {
element.tap()
isElementExist = true
break
}
}
if !isElementExist {
XCTFail("Failed to find element")
}
}
where you call the method in the test like:
tapBasedOnAccessibilityIdentifier(elementType: app.staticTexts, accessibilityIdentifier: "Accessibility Identifier")
You can tweak it a little so that it cover all the requirements.

Resources