chromium/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBareboneTest.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.content.browser;

import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper;

/** Common functionality for testing the Java Bridge. */
@RunWith(BaseJUnit4ClassRunner.class)
@Batch(JavaBridgeActivityTestRule.BATCH)
public class JavaBridgeBareboneTest {
    @Rule public JavaBridgeActivityTestRule mActivityTestRule = new JavaBridgeActivityTestRule();

    private TestCallbackHelperContainer mTestCallbackHelperContainer;

    private void injectDummyObject(final String name) throws Throwable {
        mActivityTestRule.runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        mActivityTestRule
                                .getJavascriptInjector()
                                .addPossiblyUnsafeInterface(new Object(), name, null);
                    }
                });
    }

    private String evaluateJsSync(String jsCode) throws Exception {
        OnEvaluateJavaScriptResultHelper javascriptHelper = new OnEvaluateJavaScriptResultHelper();
        javascriptHelper.evaluateJavaScriptForTests(mActivityTestRule.getWebContents(), jsCode);
        javascriptHelper.waitUntilHasValue();
        return javascriptHelper.getJsonResultAndClear();
    }

    // If inection happens before evaluating any JS code, then the first evaluation
    // triggers the same condition as page reload, which causes Java Bridge to add
    // a JS wrapper.
    // This contradicts the statement of our documentation that injected objects are
    // only available after the next page (re)load, but it is too late to fix that,
    // as existing apps may implicitly rely on this behaviour, something like:
    //
    //   webView.loadUrl(...);
    //   webView.addJavascriptInterface(new Foo(), "foo");
    //   webView.loadUrl("javascript:foo.bar();void(0);");
    //
    @Test
    @SmallTest
    @Feature({"AndroidWebView", "Android-JavaBridge"})
    public void testImmediateAddition() throws Throwable {
        injectDummyObject("testObject");
        Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
    }

    // Now here, we evaluate some JS before injecting the object, and this behaves as
    // expected.
    @Test
    @SmallTest
    @Feature({"AndroidWebView", "Android-JavaBridge"})
    public void testNoImmediateAdditionAfterJSEvaluation() throws Throwable {
        evaluateJsSync("true");
        injectDummyObject("testObject");
        Assert.assertEquals("\"undefined\"", evaluateJsSync("typeof testObject"));
    }

    @Test
    @SmallTest
    @Feature({"AndroidWebView", "Android-JavaBridge"})
    public void testImmediateAdditionAfterReload() throws Throwable {
        mActivityTestRule.synchronousPageReload();
        injectDummyObject("testObject");
        Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
    }

    @Test
    @SmallTest
    @Feature({"AndroidWebView", "Android-JavaBridge"})
    public void testReloadAfterAddition() throws Throwable {
        injectDummyObject("testObject");
        mActivityTestRule.synchronousPageReload();
        Assert.assertEquals("\"object\"", evaluateJsSync("typeof testObject"));
    }
}