Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Espresso: How to call evaluateJavascript() on a WebView

I am trying to get an instance of a WebView, so that I can call evaluateJavascript() on it. I wrote a custom matcher and then tried to assign the WebView to a static variable as below:

 public static WebView view1;

 public static Matcher<View> isJavascriptEnabled1() {
    return new BoundedMatcher<View, WebView>(WebView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("WebView with JS enabled");
        }
        @Override
        public boolean matchesSafely(WebView webView) {
            view1 =webView;
            return webView.getSettings().getJavaScriptEnabled();
        }
    };
}

And in my test class I call:

 CustomMatcher.isJavascriptEnabled1();
 CustomMatcher.view1.evaluateJavascript("$('.status dl       dd').get(0).innerText.replace('pts.', '').replace(',', '').trim()\"]",new    ValueCallback<String>() {
   @Override
   public void onReceiveValue(String s) {
       Log.d("LogName", s); // Prints asd
   }

});

I get the error: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.webkit.WebView.evaluateJavascript(java.lang.String, android.webkit.ValueCallback)' on a null object reference

like image 707
San Kh Avatar asked Jun 01 '26 04:06

San Kh


1 Answers

You are not using the Matcher to match a view. It's not working because calling isJavascriptEnabled1() just creates a new instance of your custom Matcher. But e.g. matchesSafely() is never executed and thats why CustomMatcher.view1 is null. To make your code work you have to use Espresso.onView() together with your Matcher:

 // Use onView and the matcher to find a web view with enabled java script
 Espresso.onView(CustomMatcher.isJavascriptEnabled1());

 // Now CustomMatcher.view1 should not be null (if there is a web view)
 CustomMatcher.view1.evaluateJavascript("$('.status dl       dd').get(0).innerText.replace('pts.', '').replace(',', '').trim()\"]",new    ValueCallback<String>() {
   @Override
   public void onReceiveValue(String s) {
       Log.d("LogName", s); // Prints asd
   }
});

But thats still not correct. The method evaluateJavascript() is asynchronous and Espresso will not wait for the callback (onReceiveValue()) to be called. So the test will most likely finish before onReceive() is called.

There is also Espresso Web for testing WebViews. Depending on your goal you might find it useful. Espresso Web executes java script under the hood, too.

Without using Espresso Web I would suggest to write a custom ViewAction, that will be performed on the matched WebView. This action can use the WebView to evaluate java script:

/**
 * {@link ViewAction} that evaluates javascript in a {@link WebView}.
 */
public class EvaluateJsAction implements ViewAction, ValueCallback<String> {

    private static final long TIME_OUT = 5000;
    private final String mJsString;
    private final AtomicBoolean mEvaluateFinished = new AtomicBoolean(false);

    public EvaluateJsAction(final String javaScriptString) {
        mJsString = javaScriptString;
    }

    @Override
    public Matcher<View> getConstraints() {
        return isAssignableFrom(WebView.class);
    }

    @Override
    public String getDescription() {
        return "evaluate '" + mJsString + "' on webview";
    }

    @Override
    public void perform(UiController uiController, View view) {
        uiController.loopMainThreadUntilIdle();

        final WebView webView = (WebView) view;
        webView.evaluateJavascript(mJsString, this);

        final long timeOut = System.currentTimeMillis() + TIME_OUT;
        while (!mEvaluateFinished.get()) {
            if (timeOut < System.currentTimeMillis()) {
                throw new PerformException.Builder()
                        .withActionDescription(this.getDescription())
                        .withViewDescription(HumanReadables.describe(view))
                        .withCause(new RuntimeException(String.format(Locale.US,
                                "Evaluating java script did not finish after %d ms of waiting.", TIME_OUT)))
                        .build();
            }
            uiController.loopMainThreadForAtLeast(50);
        }
    }

    @Override
    public void onReceiveValue(String value) {
        mEvaluateFinished.set(true);
    }
}

Then use this action:

onView(withJavaScriptEnabled()).perform(new EvaluateJsAction(theJavaScriptString));
like image 178
thaussma Avatar answered Jun 03 '26 19:06

thaussma



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!