Bug 1249491 - Add JavascriptBridgeTest.getBlockingFromJsString and friends. r=sebastian draft
authorMichael Comella <michael.l.comella@gmail.com>
Mon, 14 Mar 2016 16:20:14 -0700
changeset 344589 16597708dd7dcef84fb24e7a266aaa96d49405b0
parent 344387 b999fac0569d760e971b6579c420eb887d645433
child 344590 ee35637619d917f26629409667500ac218672ce4
push id13869
push usermichael.l.comella@gmail.com
push dateThu, 24 Mar 2016 22:48:32 +0000
reviewerssebastian
bugs1249491
milestone48.0a1
Bug 1249491 - Add JavascriptBridgeTest.getBlockingFromJsString and friends. r=sebastian MozReview-Commit-ID: JzG9WhZcXrp
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/JavascriptBridgeTest.java
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/JavascriptBridgeTest.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/JavascriptBridgeTest.java
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
+import static org.mozilla.gecko.tests.helpers.AssertionHelper.*;
+
 import org.mozilla.gecko.tests.helpers.GeckoHelper;
 import org.mozilla.gecko.tests.helpers.JavascriptBridge;
 import org.mozilla.gecko.tests.helpers.NavigationHelper;
 
 /**
  * Extended to write tests using JavascriptBridge, which allows Java and JS to communicate back-and-forth.
  * If you don't need back-and-forth communication, consider {@link JavascriptTest}.
  *
@@ -17,18 +19,24 @@ import org.mozilla.gecko.tests.helpers.N
  *   * Add your javascript file to the base robocop directory (see where `testJavascriptBridge.js` is located)
  *   * In the main test method, call {@link #blockForReadyAndLoadJS(String)} with your javascript file name
  * (don't include the path) or if you're loading a non-harness url, be sure to call {@link GeckoHelper#blockForReady()}
  *   * You can access js calls via the {@link #getJS()} method
  *     - Read {@link JavascriptBridge} javadoc for more information about using the API.
  */
 public class JavascriptBridgeTest extends UITest {
 
+    private static final long WAIT_GET_FROM_JS_MILLIS = 2000;
+
     private JavascriptBridge js;
 
+    // Feel free to implement additional return types.
+    private boolean isAsyncValueSet;
+    private String asyncValueStr;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
         js = new JavascriptBridge(this);
     }
 
     @Override
     public void tearDown() throws Exception {
@@ -38,9 +46,62 @@ public class JavascriptBridgeTest extend
 
     public JavascriptBridge getJS() {
         return js;
     }
 
     protected void blockForReadyAndLoadJS(final String jsFilename) {
         NavigationHelper.enterAndLoadUrl(mStringHelper.getHarnessUrlForJavascript(jsFilename));
     }
+
+    /**
+     * Used to retrieve values from js when it's required to call async methods (e.g. promises).
+     * This method will block until the value is retrieved else timeout.
+     *
+     * This method is not thread-safe.
+     *
+     * Ideally, we could just have Javascript call Java when the callback completes but Java won't
+     * listen for messages unless we call into JS again (bug 1253467).
+     *
+     * To use this method:
+     *   * Call this method with a name argument, henceforth known as `varName`. Note that it will be capitalized
+     * in all function names.
+     *   * Create a js function, `"getAsync" + varName` (e.g. if `varName == "clientId`, the function is
+     * `getAsyncClientId`) of no args. This function should call the async get method and assign a global variable to
+     * the return value.
+     *   * Create a js function, `"pollGetAsync" + varName` (e.g. `pollGetAsyncClientId`) of no args. It should call
+     * `java.asyncCall('blockingFromJsResponseString', ...` with two args: a boolean if the async value has been set yet
+     * and a String with the global return value (`null` or `undefined` are acceptable if the value has not been set).
+     */
+    public String getBlockingFromJsString(final String varName) {
+        isAsyncValueSet = false;
+        final String fnSuffix = capitalize(varName);
+        getJS().syncCall("getAsync" + fnSuffix); // Initiate async callback
+
+        final long timeoutMillis = System.currentTimeMillis() + WAIT_GET_FROM_JS_MILLIS;
+        do {
+            // Avoid sleeping! The async callback may have already completed so
+            // we test for completion here, rather than in the loop predicate.
+            getJS().syncCall("pollGetAsync" + fnSuffix);
+            if (isAsyncValueSet) {
+                break;
+            }
+
+            if (System.currentTimeMillis() > timeoutMillis) {
+                fFail("Retrieving " + varName + " from JS has timed out");
+            }
+            try {
+                Thread.sleep(100, 0);
+            } catch (final InterruptedException e) { }
+        } while (true);
+
+        return asyncValueStr;
+    }
+
+    public void blockingFromJsResponseString(final boolean isValueSet, final String value) {
+        this.isAsyncValueSet = isValueSet;
+        this.asyncValueStr = value;
+    }
+
+    private String capitalize(final String str) {
+        return str.substring(0, 1).toUpperCase() + str.substring(1);
+    }
 }