Try-catch like feature for uncertain code block in Swift 4 - swift

I am writing a test in XCUITest. There is an introductory banner which may or may not come on the mobile screen while my test is in progress. I want to write my case in such a way that, if the banner is present, I have to verify certain elements on that banner and click on the "Continue" button on it, and if it is not present, I have to proceed with my test case.
I know in Java, we write such code inside a try-catch block, so that even if the banner is not found and the code fails, the pointer goes into the catch block and the remaining program continues.
When I searched on the internet, i only found such feature for single statements and not for some chunk of code block.Do we have something similar in Swift, where I can write some code inside try-catch like code block?
Please have a look at my test case and the code to click in the banner.
class MyTestClass: BaseTest {
func testMapSearch() {
do {
let app = try XCUIApplication()
let element = try app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element(boundBy: 1).children(matching: .other).element(boundBy: 0)
element.buttons["Continue"].tap()
} catch let error {
print("Error: \(error)")
}
let logInButton = app.scrollViews.otherElements.buttons["Log In"]
checkLoginPage(login: logInButton)
logInButton.tap()
}
}
The do block is written to click on the "Continue" button on a banner which is not sure to be displayed on the screen. The do block is followed by more code. If the banner is not displayed, the code written in the do block fails and the test case throws an error. I want to write a case such that the login button code should get executed irrespective of the banner is displayed or not.

The problem with your code is that the two lines of code marked try are not failable (they don't throw), so you cannot mark them try in the first place.
The entire attempt to turn this situation into a try/catch is wrong-headed. What you want to do here is simply add a condition to check whether the desired element exists and is tappable, and proceed differently if it doesn't (i.e. don't attempt to tap it). That is what if is for.
(Of course one might argue that if the test is not intended to fail if it is impossible to tap this element, then the test itself is incorrectly constructed — you should just omit the tap line altogether.)

Related

How to click on copy button with cypress

So in my app i have a button and when i click on it, it will save some text into my clipboard, then i can past it by ctrl+v somewhere.
And i am trying to write cypress test for this. Problem is, when i click on that button i got cypress error:
I can´t figured out. Element is clearly viewed on page. I tryied to wait few second before clicking and it didn´t help.
Is there a way how to test it?
Thank you for your answers.
Your application has thrown an error, not cypress. To avoid this, you will want to add this either to your test file or /support/index.js. This code was pulled from this example. Remember cy. requires a test or a hook to work, while Cypress. does not.
// inspect the caught error
cy.on('uncaught:exception', (e) => {
if (e.message.includes('Things went bad')) {
// we expected this error, so let's ignore it
// and let the test continue
return false
}
// on any other error message the test fails
})
You can catch exceptions globally by writing this under cypress/support/index.js
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})

How to get "Area under aircraft unsuitable for landing" message?

When I initiate auto-landing in the DJI Fly app I sometimes get the following message, especially under bad lighting conditions:
Now, in my own code, when I call DJIFlightController.startLandingWithCompletion, the drone would not land and the completion block gets executed without any error.
My question is, how can I intercept the equivalent to DJIs error message as shown above? What code is relevant for that?
EDIT 1:
I am also checking if a landing confirmation is needed with the following code:
func observeConfirmLanding() {
guard let confirmLandingKey = DJIFlightControllerKey(param: DJIFlightControllerParamConfirmLanding) else { return }
DJISDKManager.keyManager()?.startListeningForChanges(on: confirmLandingKey, withListener: self) { (oldValue: DJIKeyedValue?, newValue: DJIKeyedValue?) in
DispatchQueue.main.async {
if let oldBoolValue = oldValue?.boolValue,
let newBoolValue = newValue?.boolValue,
oldBoolValue != newBoolValue {
self.landingConfirmationNeeded = newBoolValue
self.logger.debug("Landing confirmation is needed")
}
}
}
}
It never enters the closure.
As I understood the landing confirmation might be needed at a height of 0.3m, but in my case, the landing process gets interrupted at different heights that are more than 0.3m, e.g. already at 2m or 1.5m
EDIT 2:
I have changed the surface below the drone in my basement by adding a bright carpet with a distinct pattern. This improves the whole stability of the drone AND even more important: The drone just lands without being interrupted. I do not get the warning message in the DJI Fly app any more.
I check for isLandingConfirmation the way Brien suggests in his comment, I finally get true when testing this in the simulator.
extension FlightControllerObserver: DJIFlightControllerDelegate {
func flightController(_ fc: DJIFlightController, didUpdate state: DJIFlightControllerState) {
if (landingConfirmationNeeded != state.isLandingConfirmationNeeded) {
landingConfirmationNeeded = state.isLandingConfirmationNeeded
}
}
But, when I test this in my basement (flight mode "OPTI") and outside (flight mode "GPS") the drone just lands without waiting for any confirmation.
While I learned a lot, it is still a miracle to me which class in the DJI Mobile SDK is responsible for "throwing" that warning message.
If the completion handler completes without any error you might need to check if isLandingConfirmationNeeded in DJIFlightControllerState is set to true. If thats the case then you will need to implement the function confirmLandingWithCompletion.
Sounds relevant to your experience looking at the documentation
(void)confirmLandingWithCompletion:(DJICompletionBlock)completion Confirms continuation of landing action. When the clearance between
the aircraft and the ground is less than 0.3m, the aircraft will pause
landing and wait for user's confirmation. Can use
isLandingConfirmationNeeded in DJIFlightControllerState to check if
confirmation is needed. It is supported by flight controller firmware
3.2.0.0 and above.
The landingProtectionState property of the DJIVisionControlState class could be a good place to look for the cause of that error message. One of the potential states that sounds relevant is
DJIVisionLandingProtectionStateNotSafeToLand -> Landing area is not flat
enough to be considered safe for landing. The aircraft should be moved
to an area that is more flat and an auto-land should be attempted
again or the user should land the aircraft manually.
Also within a section of DJI's documentation there is a section on an article about flight control that talks about landing protection and forcing a landing. I couldn't see any functions in the SDK to force a landing.
You should be aware of that the fly app does not use the sdk internally. It uses the middlelayer directly.
You often get different behaivor when using the SDK compared to the app. Some functions are not available at all.
I usually disable it completly, I want it to land when I say so :-)
(exit_landing_ground_not_smooth_enable g_config.landing.exit_landing_ground_not_smooth_enable)

"Neither element nor any descendant has keyboard focus" when running XCTestCase in a real iPhone

I'm trying to run a UI test case where there are two input fields exists. Following is my code
let usernameTextField = app.webViews.otherElements["Identity Server"].textFields["Username"]
let passwordField = app.webViews.otherElements["Identity Server"].secureTextFields["Password"]
_ = usernameTextField.waitForExistence(timeout: 8)
usernameTextField.tap()
usernameTextField.typeText("TEST") // Breakpoint line
app.typeText("\n")
passwordField.tap()
passwordField.typeText("test")
When I run the test case normally it is failing with the error given in the question title. But if I add a breakpoint to the commented line, it will run without any error.
I tried to use following code snippets after the breakpoint line, separately.
sleep(8)
_ = passwordField.waitForExistence(timeout: 8)
But non of those work. As for further information, this is a Auth process scenario where those input fields resides in a web view.
I decided to answer myself rather than closing the question. I'll tell what went wrong in my code. The main mistake I have done was continueAfterFailure set as true. Because of that, the error shown in the wrong line not the actual error throwing line.
So the solution is,
continueAfterFailure = false
and
usernameTextField.tap()
sleep(2)
usernameTextField.typeText("TEST")
There should be a small waiting time till keyboard appears in the web view before type text.
Send the \n on the end of the string you send to the username text field:
usernameTextField.typeText("TEST\n")

How to know any UI rendering is completed in automation code

I am wanting to know a button is rendered on main window UI or not. This button rendering is depending on server response result (written in Objective C). If server response comes perfectly it becomes render perfectly (VISIBLE) otherwise it is not present there (INVISIBLE). And whenever it becomes visible I always tap on it for further next process.
I wrote code
UIATarget.localTarget().pushTimeout(200);
//My code
UIATarget.localTarget().popTimeout();
By the above code I have to wait till 200 sec but my concern is I want to wait but whenever object is on screen I don't want keep me busy in WAITING MODE.
How will I write code in automation?
Thanks
Ok, this might give you idea how to follow-up:
For your view implement an accessibilityValue method which returns a JSON formatted value:
- (NSString *)accessibilityValue
{
return [NSString stringWithFormat:
#"{'MyButtonisVisible':%#}",
self.MyButton.isHidden ? #"false" : #"true"];
}
Then somehow you can access it from your test javascript:
var thisproperty = eval("(" + element.value() + ")");
if (thisproperty.MyButtonisVisible) {
UIATarget.localTarget().tap({"x":100, "y":100});
}
Hope that helps.
If you make the name different when you enable the button you can do this:
var awesomeButton = target.frontMostApp().mainWindow().buttons()[0];
UIATarget.localTarget().pushTimeout(200);
awesomeButton.withName("My Awesome Button");
if (awesomeButton.isVisible()) {
UIALogger.logError("Error no awesome button!");
}
UIATarget.localTarget().popTimeout();
withName will repeatedly test the name and control will return to your script once the name matches or when the time out is reached.
Per Apple's Doc
withName:
Tests if the name attribute of the element has the given string value. If the match fails, the test is retried until the current timeout expires.
Timeout Periods:
If the action completes during the timeout period, that line of code returns, and your script can proceed. If the action doesn’t complete during the timeout period, an exception is thrown.
https://developer.apple.com/library/etc/redirect/xcode/ios/e808aa/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html#//apple_ref/doc/uid/TP40004652-CH20

How can I pause execution within a method until the user sends a "Did End on Exit" (presses the "Next" button on the keyboard?

This is what I have:
while(wordList){ //wordlist is instance of NSArray containing NSStrings
word.text = [wordList objectAtIndex:x]; //word is instance of UILabel
//LOOKING TO PLACE WAIT CODE HERE TO WAIT FOR "DID END ON EXIT"
input = inputBox.text; //input is instance of UITextField
[self compare:input andb:word.text]; //compare is an instance method to compare the two strings
x++;
}
I'm a beginner, if any of you could help me, that would be fantastic.
Best...
SL
Is this what you are looking for?:
[yourTextField addTarget:self
action:#selector(yourMethod:)
forControlEvents:UIControlEventEditingDidEndOnExit];
Edit:
Why don't you just break the while loop and call a separate method when the user hits next? No need to pause and trigger some other method.
The approach you're asking about won't work. If your app were to pause execution in the middle of a method, what would cause it to unpause?
The user is not going to be entering text in the middle of your loop -- that would have to have already done that before the loop starts to execute. Trigger the method that contains the loop as #Imirak suggested, or from some other user interaction, for example a button press. That way you'll know that the user has already entered the text, and the code you've written should work as expected.
One note though: the code you've shown doesn't check the return value from your compare:andb: method. You haven't really provided enough information about what you expect the method to do to do to be sure whether that makes sense or not.
Also, your loop control logic is incorrect -- as written it's either going to be an infinite loop or it will never be entered, depending on whether wordList is nil. Consider using fast enumeration syntax instead of writing a while loop, for example:
for (UILabel *currLabel in wordList)
{
// It appears as though this may be the comparison you want to do
// but there's not enough context in your question to be sure.
//
if ([inputBox.text isEqualToString:currLabel.text])
{
// Do something here. Again, it's not clear what you're trying to do.
}
}