Spying on JavaScript (or non-HTTP calls) in Capybara

I have an odd scenario that I can’t figure out how to test.

iOS has an annoying behavior where if a user declines push messages, they are effectively locked out of receiving pushes for a long time. There are ways around it, but they involve resetting the phone’s date, uninstalling the app, and a bunch of things that are extremely unreasonable to ask a user to perform on their phone.

Push messaging is really important in our application, so we don’t want to display the Apple push registration confirmation dialog until we are absolutely sure the user will say yes. To accomplish this check, in our webview-driven app, we display our own dialog to the user explaining to them what they get from accepting push messages, and then if they accept, we use JockeyJS to send a message to the iOS app from our webview session telling the app that it’s OK to show the Apple dialog. Jockey accomplishes this communication between the webview and Objective-C code through the use of a custom URL structure (it’s jockey://) where calls are made that pass between the JavaScript and Objective-C domains.

I am at a loss for how to test this other than just using my fingers and eyes. My first instinct was to do an integration test, but since the dialog displayed is iOS and not part of the DOM, I can’t use Capybara matchers to test whether or not the iOS dialog has been triggered by the HTML dialog. This behavior is fairly tightly coupled to the browser, so I’m having trouble writing Jasmine specs that prove the behavior is correct.

I should add that the simulator sometimes “remembers” your push message registration state, so you can’t just re-run tests in iOS easily as far as I can tell.

There are a couple of ways I’d be satisfied that there’s coverage here:

  1. Confirm a call was made to a jockey:// URL from the app (can one listen for this in RSpec/Capybara?)
  2. Confirm a certain browser event was fired (Jockey’s events are like jQuery’s so you define them with Jockey.on() and trigger them with Jockey.trigger()).

Any Capybara superheroes with any ideas of how to properly test this scenario?

My goto for things like this is @jferris. Any ideas Joe-dawg?

Listening for calls to the jockey:// protocol is going to be difficult, because your fake browser (Selenium/Firefox or WebKit) won’t try to use an HTTP request to make those calls. It will check to see if anything is registered for that protocol and then fail.

It would be possible to compile a custom version of capybara-webkit to handle custom protocols, but that may be more trouble than it’s worth.

I think the easiest thing to do would be to create a fake version of the Jockey JavaScript client and make sure it gets loaded during tests instead of the real library. You can then record something on the window object which says that a push notification would have been sent. You can use Capybara’s evaluate_javascript to look for recorded invocations.

3 Likes

That’s a great idea, thanks!