IOS UI Testing: How do I determine if the app under test switched to Settings App? - ios11

My App uses Location Services and I have code that alerts the user if Location Services are disabled. This has a button that allows them to switch to the Settings app and enable it. This works fine but I want to write a UI Test that detects it, if possible.
Currently, my UI Tests work correctly checking for the alert appearing and that it has a Settings button. I can confirm the app switches to settings through either the simulator or physical device when the button is tapped.
I'm not sure I can tell that the Settings app itself appears through the UITest (that would be nice!) but I could check that the app under test entered the background (that would be good enough!)
In my AppDelegate I have print statements that confirm the app is going through applicationWillResignActive(...) and applicationDidEnterBackground(...)
I tried the following:
let predicate = NSPredicate(format: "self.state = XCUIApplication.State.runningBackground", argumentArray: nil)
_ = self.expectation(for: predicate, evaluatedWith: app, handler: nil)
waitForExpectations(timeout: 10.0, handler: nil)
XCTAssertTrue(app.state == .runningBackground, "Doesn't look like the Settings App was launched")
and the console is flagging up that it is waiting on the expectation - it logs every second. Note that 'app' is the instance of XCUIApplication() that I use in the test. Eventually, the expectation times out and the test terminates because it wasn't fulfilled. At that point, my two print statements (resigning active, entered background) appear on the console.
It would seem that my app is working as expected but that the expectation isn't determining the change in app state. I've tried setting the wait period to 60 secs but the result is the same so it isn't a case of not waiting long enough.
Without the expectation, i.e. the code goes straight from pressing the settings button to checking the assert, the assert fails because the app state is .runningForeground. I thought that execution was happening too fast, hence the attempt to wait for the state change. In this case, the assert fails, the test finishes and then "Resigning active" appears in the console, but not "Entered background".
This is ios11 on Xcode 9 and behaviour is the same in the simulator and physical device.
To sum up, the question is: what's the best way to programmatically test that the Settings app has launched?

So it would seem I'm overcomplicating it! Whilst waiting for an answer I thought I'd see if I could check for a UI Element becoming inactive, or similar. That's when I discovered:
app.wait(for: .runningBackground, timeout: 10)
Which returns true when that state is achieved! And it works. Obviously, I don't know whether the app now running is Settings but it's good enough as the only reason for that state to be achieved in that UI Test is if the settings URL activated.

Related

HLS+Fairplay stream resource freeing issue

The Context
I'm using the HTML video element to play HLS+Fairplay streams (VOD and live) in Safari.
My application is a Netflix-like. You can watch a video, stop, and watch another one.
Concretly, the <video> tag is created upon each play and destroyed when the player exits.
The Problem
Sometimes, after exiting the player, launching a new video causes a MEDIA_ERR_DECODE error without anymore explation.
I can trace in my code that the Fairplay certificate and the CKC have been correctly retrieved and that the media session has been created and updated (with the CKC).
In fact, when I log everything, I see the exact same logs as when it works. Except the final MEDIA_ERR_DECODE error.
When I get this error, if I immediately retry to play, the same error happens, but if I wait about 5~10 seconds, it successfully plays. It's like some resource is waiting to be garbage-collected.
What I tried
I tried playing non-DRMed videos and it works perfectly: I could play, stop, play, etc. without any issue.
So my guess is something is happening at the EME level.
I've browse the whole Internet (twice) and couldn't come up with a clear explanation.
Most of the hints I found couldn't explain my issue (e.g. bad DRM-encryption, but how could it work the first time?) or solve it (e.g. videoElement.src = "").
I'm throwing a bottle into the sea, here...

Move specific application to a specific screen

I have read over here how to move an application to a specific screen.
In my case I have a variation of this. In this case I want to open for example Todoist on a specific screen. This code below opens Todoist but on my wrong screen.
How can I solve this?
local screens = hs.screen.allScreens()
hs.application.open("Todoist")
local win = hs.application:findWindow("Todoist")
win.moveToScreen(screens[1])
findWindow() is an instance method, so it cannot be called directly as hs.application:findWindow(). To properly call this method, you must create an instance of the hs.application class and then call findWindow() on that instance.
The following snippet should work, although you may need to adjust the wait time (and the screens index). It is generally recommended to use hs.application.watcher to watch for when an app has been launched, rather than using a timer.
local notes = hs.application.open("Notes")
hs.timer.doAfter(1, function()
-- `notes:mainWindow()` will return `nil` if called immediately after opening the app,
-- so we wait for a second to allow the window to be launched.
local notesMainWindow = notes:mainWindow()
local screens = hs.screen.allScreens()
notesMainWindow:moveToScreen(screens[1])
end)

After reloading CN1 Form, GUI elements stop working properly

Our app allows users to submit data to our servers.
Sometimes, users will need to make 3 submission in a row. After checking that the submission works properly (and thankfully it does), we call CustomForm().show() and the Form shows correctly. (CustomForm obviously extends Form.)
We use InteractionDialogs, just as in all other iterations, to handle validation by providing feedback to the user. We do so to mimic the Android Snackbar that provides a prompt and a Button to do an action. In the 1st 2 iterations, the InteractionDialogs show and dispose properly; in the 3rd iteration, the InteractionDialogs start acting erratically.
We also noticed that once that CustomForm (or any other submission forms) is shown 3 times (either the same Form 3 times in a row, or any combo of our submission forms 3 times altogether), a different Form displaying a BrowserComponent will fail – the BrowserComponent will not show anything at all, despite using a strong, unfiltered internet connection and having worked properly prior to the submissions being done. Neither the app nor the Form housing the BrowserComponent crashes -- the BrowserComponent simply does not show anything.
Is there a limit on how many instances of a given Form can be shown, or is there a memory issue?
Please, any ideas on how to solve this?
Thank you.
EDIT: I am adding a screenshot of the EDT thread -- it seems to have invokeAndBlock() called twice and then wait() is called, even though I wrapped my call to show the Form with the BrowserComponent with this try-catch:
try {
CN.invokeWithoutBlocking(() -> {
new FormWithBrowser().show();
)};
} catch (BlockingDisallowedException ex) {
e.getMessage();
}
The other issue is how do I fix InteractionDialogs in my submission form from being affected by invokeAndBlock() and wait() as well? I also don't get any BlockingDisallowedExceptions in my logs. Does that mean that wait() is really the issue?
Thanks again.

Prompt when trying to dial a phone number using tel:// scheme on iOS 11

I am facing an issue, while trying to start a phone call from my iOS app using:
UIApplication open(_:options: completionHandler:)
iOS shows an confirmation popup/alert before starting the call with two button Call & Cancel and CompletionHandler called with a Bool parameter having true/false based on button action in iOS 10.
But now in iOS11 the problem is that the completionHandler is being called automatically before tapping on "Cancel" or "Call" button of confirmation popup, always having true value.
Is this a bug in iOS11 or is there something that I am doing wrong?
There has been a behavior change in when the closure is called in iOS 11. I cant say if this behavior will be reverted or if this is a bug.
But one of the ways you can identify when the user interacted with the popup is by adding a notification listener around UIApplicationDidBecomeActive in the completion closure of openURL(). To identify whether the call was clicked or not you will need to create another custom notification and listener on the CTCallCenter. That was the best way through which I was able to identify it, there might be better solutions though :D
completionHandler will get a call if your given URL gets open success or failure, this has nothing to do with Cancel & Call buttons on Alert
see what Apple docs has to say about it HERE
completionHandler
The block to execute with the results. Provide a
value for this parameter if you want to be informed of the success or
failure of opening the URL. This block is executed asynchronously on
your app's main thread. The block has no return value and takes the
following parameter:
success
A Boolean indicating whether the URL was
opened successfully.

Key capture directive not working as intended in Angularjs

i am trying to capture keypress events in my angular app.
The intended functionality is that upon pressing g1 (i.e simulataneously pressing g and 1) the app should transition to state1. Upon pressing g2 the app should transition to state2 and so on.
However the app does not seem to be capturing two keypresses simulataneously. What am I missing here ?
Also is this the best way to achieve this or are there better solutions available.
You can see the plnkr here
http://plnkr.co/edit/sK0NYNDRtH4lFfteNd5O
You could pass the e.codeValue to a service/controller which then stores the keys pressed in an array.
On key press up, tell the service/controller to remove it from the "key pressed" array.
In the service/controller, you can then use your logic to see if g and 1 are in the array - if they are, do the state transition. That could work, although not 100% if it's the best way. Give it a try and let us know if it worked for you.
Javascript is single-threaded, so there is no way for the following if statement to ever run the true block:
if (x == 3 && x == 4) {
// true
} else {
// false
}
Aside from that, the real problem you're facing is that (to my knowledge) the browser does not directly expose rollover scan codes to Javascript. By the time the browser gets the keystrokes they're already de-rollovered (if that's a word).
BTW, style note: It's better to post your code here on Stack Overflow to save it for posterity for other readers to learn from. Adding a plunkr is a nice courtesy, but it's secondary.

Resources