This is an automated email from the ASF dual-hosted git repository.

GitToTheHub pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-android.git


The following commit(s) were added to refs/heads/master by this push:
     new 1e4ad6c6 test(android): repair navigation and message-channel 
regressions (#2038)
1e4ad6c6 is described below

commit 1e4ad6c610422e1c202eedc1a4ab33746d3657bf
Author: Manuel Beck <[email protected]>
AuthorDate: Tue Nov 3 12:24:51 2026 +0100

    test(android): repair navigation and message-channel regressions (#2038)
    
    Repair the instrumentation suite's navigation and message-channel tests so
    they exercise Cordova behavior reliably with the updated AndroidX runner.
    
    Serve fixtures from the test app's HTTPS asset origin and replace fixed
    sleeps and unbounded waits with bounded page and JavaScript readiness
    checks. Tap fixture links using real touch input and DOM coordinates;
    dispatch Back key events directly to the WebView for view-specific history
    coverage instead of relying on an obsolete view ID or the system Back path.
    
    Replace the iframe test's external Google Maps dependency with local pages.
    Exercise error handling by loading a missing local file after a valid page,
    then assert the native error callback and HTTPS fallback without assuming
    failed-page callback ordering. Launch each test activity only once.
    
    Verify supported message-channel event delivery before and after a full
    reload, ensuring the new document receives exactly one event. A negative
    control retaining the stale cached CoreAndroid instance fails after reload,
    confirming the repaired test detects that regression.
    
    With the build/theme and AndroidX dependency repairs from #2035 and #2037,
    all 16 instrumentation tests previously passed on Android 7.1.1 (API 25),
    Android 16 (API 36), and Android 17 (API 37), without test filtering or a
    blanket runner timeout. After resolving the merge conflict, lint, all 337
    JavaScript specs, Java unit tests, and both complete debug APK builds pass.
    These changes are limited to the test app, fixtures, and instrumentation
    sources; Cordova runtime behavior is unchanged.
    
    Generated-by: GPT-6.1 Sol
---
 .../cordova/unittests/BackButtonMultipageTest.java |  73 +++++++------
 .../org/apache/cordova/unittests/ErrorUrlTest.java |  49 ++++-----
 .../org/apache/cordova/unittests/IFrameTest.java   | 100 +++++------------
 .../unittests/MessageChannelMultipageTest.java     | 118 ++++++---------------
 .../apache/cordova/unittests/WebViewTestUtils.java | 104 ++++++++++++++++++
 .../main/assets/www/backbuttonmultipage/index.html |   2 +-
 .../assets/www/backbuttonmultipage/sample2.html    |   2 +-
 .../src/main/assets/www/htmlnotfound/index.html    |  20 ++++
 .../app/src/main/assets/www/iframe/frame1.html     |  20 ++++
 .../app/src/main/assets/www/iframe/index.html      |  38 ++-----
 .../app/src/main/assets/www/iframe/index2.html     |  26 +----
 .../src/main/assets/www/messagechannel/index.html  |  32 ++++++
 .../org/apache/cordova/unittests/TestActivity.java |   8 ++
 test/androidx/app/src/main/res/xml/config.xml      |   2 +-
 14 files changed, 312 insertions(+), 282 deletions(-)

diff --git 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
index 38c67342..6764fab0 100644
--- 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
+++ 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
@@ -20,6 +20,9 @@
 package org.apache.cordova.unittests;
 
 import android.content.Intent;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+import android.view.View;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -29,58 +32,43 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.pressBack;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.web.sugar.Web.onWebView;
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 
-import static org.apache.cordova.unittests.R.id.cordovaWebView;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitPageFinished;
+import static org.apache.cordova.unittests.WebViewTestUtils.clickElement;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitJavascript;
 
 
 @RunWith(AndroidJUnit4.class)
 public class BackButtonMultipageTest {
 
-    private static final String START_URL = 
"file:///android_asset/www/backbuttonmultipage/index.html";
-    private static final String SAMPLE3_URL = 
"file:///android_asset/www/backbuttonmultipage/sample3.html";
-    private static final String SAMPLE2_URL = 
"file:///android_asset/www/backbuttonmultipage/sample2.html";
-    //I have no idea why we picked 100, but we did.
-    private static final int WEBVIEW_ID = 100;
+    private static final String START_URL = 
"https://localhost/backbuttonmultipage/index.html";;
+    private static final String SAMPLE3_URL = 
"https://localhost/backbuttonmultipage/sample3.html";;
+    private static final String SAMPLE2_URL = 
"https://localhost/backbuttonmultipage/sample2.html";;
     private TestActivity mActivity;
 
     // Don't launch the activity, we're going to send it intents
     @Rule
-    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+    public ActivityTestRule<TestActivity> mActivityRule = new 
ActivityTestRule<>(
             TestActivity.class, true, false);
 
     @Before
     public void launchApplicationWithIntent() {
         Intent intent = new Intent();
         intent.putExtra("startUrl", START_URL);
-        mActivity = (TestActivity) mActivityRule.launchActivity(intent);
+        mActivity = mActivityRule.launchActivity(intent);
     }
 
     @Test
     public void testViaHref() throws Throwable {
         final CordovaWebView webInterface = mActivity.getWebInterface();
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        assertPageSample(START_URL);
 
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                webInterface.sendJavascript("window.location = 
'sample2.html';");
-            }
-        });
+        clickElement(mActivityRule, mActivity, "next_page");
         assertPageSample(SAMPLE2_URL);
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                webInterface.sendJavascript("window.location = 
'sample3.html';");
-            }
-        });
+        clickElement(mActivityRule, mActivity, "next_page");
         assertPageSample(SAMPLE3_URL);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
@@ -95,7 +83,7 @@ public class BackButtonMultipageTest {
                 assertTrue(webInterface.backHistory());
             }
         });
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        assertPageSample(START_URL);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -107,7 +95,7 @@ public class BackButtonMultipageTest {
     @Test
     public void testViaLoadUrl() throws Throwable {
         final CordovaWebView webInterface = mActivity.getWebInterface();
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        assertPageSample(START_URL);
 
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
@@ -136,7 +124,7 @@ public class BackButtonMultipageTest {
                 assertTrue(webInterface.backHistory());
             }
         });
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        assertPageSample(START_URL);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -148,7 +136,7 @@ public class BackButtonMultipageTest {
     @Test
     public void testViaBackButtonOnView() throws Throwable {
         final CordovaWebView webInterface = mActivity.getWebInterface();
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        assertPageSample(START_URL);
 
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
@@ -164,13 +152,28 @@ public class BackButtonMultipageTest {
             }
         });
         assertPageSample(SAMPLE3_URL);
-        onView(withId(WEBVIEW_ID)).perform(pressBack());
+        pressBackOnWebView();
         assertPageSample(SAMPLE2_URL);
-        onView(withId(WEBVIEW_ID)).perform(pressBack());
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+        pressBackOnWebView();
+        assertPageSample(START_URL);
+    }
+
+    private void pressBackOnWebView() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            View view = mActivity.getWebInterface().getView();
+            long time = SystemClock.uptimeMillis();
+            // Espresso's global Back action can finish the activity through 
the
+            // system dispatcher. This test specifically covers WebView key 
handling.
+            assertTrue(view.dispatchKeyEvent(new KeyEvent(time, time,
+                    KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0)));
+            assertTrue(view.dispatchKeyEvent(new KeyEvent(time, time,
+                    KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0)));
+        });
     }
 
-    private void assertPageSample(String url) throws InterruptedException {
-        assertEquals(url, mActivity.onPageFinishedUrl.take());
+    private void assertPageSample(String url) throws Throwable {
+        awaitPageFinished(mActivity, url);
+        awaitJavascript(mActivityRule, mActivity,
+                "document.getElementById('deviceready').textContent === 
'fired'");
     }
 }
diff --git 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
index 19da6fde..e411b6a9 100644
--- 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
+++ 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
@@ -17,53 +17,44 @@
     under the License.
 */
 
-
 package org.apache.cordova.unittests;
 
 import android.content.Intent;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.apache.cordova.CordovaWebView;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static junit.framework.Assert.assertEquals;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitPageFinished;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitJavascript;
 
 @RunWith(AndroidJUnit4.class)
 public class ErrorUrlTest {
-    private static final String START_URL = 
"file:///android_asset/www/htmlnotfound/index.html";
-    private static final String ERROR_URL = 
"file:///android_asset/www/htmlnotfound/error.html";
+    private static final String START_URL = 
"https://localhost/htmlnotfound/index.html";;
+    private static final String ERROR_URL = 
"https://localhost/htmlnotfound/error.html";;
     private static final String INVALID_URL = 
"file:///android_asset/www/invalid.html";
 
-    //I have no idea why we picked 100, but we did.
-    private static final int WEBVIEW_ID = 100;
-    private TestActivity mActivity;
-
     @Rule
-    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
-
-    @Before
-    public void launchApplicationWithIntent() {
-        Intent intent = new Intent();
-        intent.putExtra("startUrl", START_URL);
-        intent.putExtra("errorurl", INVALID_URL);
-        intent.putExtra("url", INVALID_URL);
-        mActivity = (TestActivity) mActivityRule.launchActivity(intent);
-    }
+    public ActivityTestRule<TestActivity> activityRule = new 
ActivityTestRule<>(TestActivity.class, true, false);
 
     @Test
     public void errorUrlTest() throws Throwable {
-        assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
-        assertEquals(ERROR_URL, mActivity.onPageFinishedUrl.take());
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertEquals(ERROR_URL, mActivity.getWebInterface().getUrl());
-            }
-        });
+        Intent intent = new Intent();
+        intent.putExtra("startUrl", START_URL);
+        TestActivity activity = activityRule.launchActivity(intent);
+        awaitPageFinished(activity, START_URL);
+        // A missing local file produces a real WebView load error without 
network I/O.
+        activityRule.runOnUiThread(() -> 
activity.getWebInterface().loadUrl(INVALID_URL));
+
+        assertEquals(INVALID_URL, activity.onReceivedErrorUrl.poll(10, 
TimeUnit.SECONDS));
+        // WebView need not finish the failed document before Cordova opens 
the fallback.
+        awaitPageFinished(activity, ERROR_URL);
+        awaitJavascript(activityRule, activity, "document.title === 'Expected 
Error'");
+        activityRule.runOnUiThread(() -> assertEquals(ERROR_URL, 
activity.getWebInterface().getUrl()));
     }
 }
diff --git 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
index 6fd05ec7..1af04790 100644
--- 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
+++ 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
@@ -17,101 +17,49 @@
     under the License.
 */
 
-
 package org.apache.cordova.unittests;
 
 import android.content.Intent;
-import androidx.test.espresso.web.webdriver.Locator;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.apache.cordova.CordovaWebView;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.pressBack;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.web.sugar.Web.onWebView;
-import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
-import static androidx.test.espresso.web.webdriver.DriverAtoms.webClick;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitPageFinished;
+import static org.apache.cordova.unittests.WebViewTestUtils.clickElement;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitJavascript;
 
 @RunWith(AndroidJUnit4.class)
 public class IFrameTest {
+    private static final String START_URL = 
"https://localhost/iframe/index.html";;
 
-    private static final String START_URL = 
"file:///android_asset/www/iframe/index.html";
-    //I have no idea why we picked 100, but we did.
-    private static final int WEBVIEW_ID = 100;
-    private int WEBVIEW_LOAD_DELAY = 500;
-
-    private TestActivity testActivity;
-
-    // Don't launch the activity, we're going to send it intents
     @Rule
-    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
-            TestActivity.class, true, false);
+    public ActivityTestRule<TestActivity> activityRule = new 
ActivityTestRule<>(TestActivity.class, true, false);
 
-    @Before
-    public void launchApplicationWithIntent() {
-        Intent intent = new Intent();
-        intent.putExtra("startUrl", START_URL);
-        testActivity = (TestActivity) mActivityRule.launchActivity(intent);
+    private void awaitFrame(TestActivity activity, String page) throws 
Throwable {
+        awaitJavascript(activityRule, activity,
+                
"document.getElementById('iframe').contentWindow.location.pathname === 
'/iframe/" + page + "'");
+        activityRule.runOnUiThread(() -> assertEquals(START_URL, 
activity.getWebInterface().getUrl()));
     }
 
     @Test
     public void iFrameHistory() throws Throwable {
-        final CordovaWebView cordovaWebView = (CordovaWebView) 
testActivity.getWebInterface();
-        onWebView().withElement(findElement(Locator.ID, 
"google_maps")).perform(webClick());
-        sleep(WEBVIEW_LOAD_DELAY);
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run()
-            {
-                String url = cordovaWebView.getUrl();
-                assertTrue(url.endsWith("index.html"));
-            }
-        });
-        sleep(WEBVIEW_LOAD_DELAY);
-        onWebView().withElement(findElement(Locator.ID, 
"javascript_load")).perform(webClick());
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run()
-            {
-                String url = cordovaWebView.getUrl();
-                assertTrue(url.endsWith("index.html"));
-            }
-        });
-        sleep(WEBVIEW_LOAD_DELAY);
-        //Espresso will kill the application and not trigger the backHistory 
method, which correctly
-        //navigates the iFrame history.  backHistory is tied to the back 
button.
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run()
-            {
-                assertTrue(cordovaWebView.backHistory());
-                String url = cordovaWebView.getUrl();
-                assertTrue(url.endsWith("index.html"));
-                assertFalse(cordovaWebView.backHistory());
-            }
-        });
-
-    }
-
-
-    //BRUTE FORCE THE CRAP OUT OF CONCURRENCY ERRORS
-    private void sleep(int timeout) {
-        try {
-            Thread.sleep(timeout);
-        } catch (InterruptedException e) {
-            fail("Unexpected Timeout");
-        }
+        Intent intent = new Intent();
+        intent.putExtra("startUrl", START_URL);
+        TestActivity activity = activityRule.launchActivity(intent);
+        awaitPageFinished(activity, START_URL);
+
+        clickElement(activityRule, activity, "first_page");
+        awaitFrame(activity, "frame1.html");
+        clickElement(activityRule, activity, "second_page");
+        awaitFrame(activity, "index2.html");
+        activityRule.runOnUiThread(() -> 
assertTrue(activity.getWebInterface().backHistory()));
+        awaitFrame(activity, "frame1.html");
+        activityRule.runOnUiThread(() -> 
assertFalse(activity.getWebInterface().backHistory()));
     }
-
 }
diff --git 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
index 8009717e..8143039b 100644
--- 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
+++ 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
@@ -23,109 +23,55 @@ import android.content.Intent;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import org.apache.cordova.CallbackContext;
-import org.apache.cordova.CordovaWebView;
 import org.apache.cordova.CordovaWebViewImpl;
-import org.apache.cordova.PluginManager;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.fail;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitPageFinished;
+import static org.apache.cordova.unittests.WebViewTestUtils.awaitJavascript;
 
 @RunWith(AndroidJUnit4.class)
 public class MessageChannelMultipageTest {
-    private static final String START_URL = 
"file:///android_asset/www/backbuttonmultipage/index.html";
-    //I have no idea why we picked 100, but we did.
-    private static final int WEBVIEW_ID = 100;
-    private TestActivity testActivity;
+    private static final String START_URL = 
"https://localhost/messagechannel/index.html";;
 
     @Rule
-    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
-            TestActivity.class);
+    public ActivityTestRule<TestActivity> activityRule = new 
ActivityTestRule<>(TestActivity.class, true, false);
 
-    @Before
-    public void launchApplicationWithIntent() {
-        Intent intent = new Intent();
-        intent.putExtra("startUrl", START_URL);
-        testActivity = (TestActivity) mActivityRule.launchActivity(intent);
-    }
-
-
-
-    //test that after a page load the cached callback id and the live callback 
id match
-    //this is to prevent a regression
-    //the issue was that CordovaWebViewImpl's cached instance of CoreAndroid 
would become stale on page load
-    //this is because the cached instance was not being cleared when the 
pluginManager was reset on page load
-    //the plugin manager would get a new instance which would be updated with 
a new callback id
-    //the cached instance's message channel callback id would become stale
-    //effectively this caused message channel events to not be delivered
-    @Test
-    public void testThatCachedCallbackIdIsValid() throws Throwable {
-        final CordovaWebView cordovaWebView = testActivity.getWebInterface();
-        Class cordovaWebViewImpl = CordovaWebViewImpl.class;
-        //send a test event - this initializes cordovaWebViewImpl.appPlugin 
(the cached instance of CoreAndroid)
-        Method method = 
cordovaWebViewImpl.getDeclaredMethod("sendJavascriptEvent", String.class);
+    private void sendTestEvent(TestActivity activity) throws Throwable {
+        // Exercise CordovaWebViewImpl's cached CoreAndroid path, rather than 
calling
+        // PluginManager directly and bypassing the cache this regression 
covers.
+        Method method = 
CordovaWebViewImpl.class.getDeclaredMethod("sendJavascriptEvent", String.class);
         method.setAccessible(true);
-        method.invoke(cordovaWebView, "testEvent");
-        sleep(1000);
-
-        //load a page - this resets the plugin manager and nulls 
cordovaWebViewImpl.appPlugin
-        //(previously this resets plugin manager but did not null 
cordovaWebViewImpl.appPlugin, leading to the issue)
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                cordovaWebView.loadUrl(START_URL);
+        activityRule.runOnUiThread(() -> {
+            try {
+                method.invoke(activity.getWebInterface(), "menubutton");
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError(e);
             }
         });
-        assertEquals(START_URL, testActivity.onPageFinishedUrl.take());
-
-        //send a test event - this initializes cordovaWebViewImpl.appPlugin 
(the cached instance of CoreAndroid)
-        method.invoke(cordovaWebView, "testEvent");
-        sleep(1000);
-
-        //get reference to package protected class CoreAndroid
-        Class coreAndroid = Class.forName("org.apache.cordova.CoreAndroid");
-
-        //get cached CoreAndroid
-        Field appPluginField = 
cordovaWebViewImpl.getDeclaredField("appPlugin");
-        appPluginField.setAccessible(true);
-        Object cachedAppPlugin = appPluginField.get(cordovaWebView);
-        //get cached CallbackContext
-        Field messageChannelField = 
coreAndroid.getDeclaredField("messageChannel");
-        messageChannelField.setAccessible(true);
-        CallbackContext cachedCallbackContext = (CallbackContext) 
messageChannelField.get(cachedAppPlugin);
-
-        //get live CoreAndroid
-        PluginManager pluginManager = cordovaWebView.getPluginManager();
-        Field coreAndroidPluginNameField = coreAndroid.getField("PLUGIN_NAME");
-        String coreAndroidPluginName = (String) 
coreAndroidPluginNameField.get(null);
-        Object liveAppPlugin = pluginManager.getPlugin(coreAndroidPluginName);
-        //get live CallbackContext
-        CallbackContext liveCallbackContext = (CallbackContext) 
messageChannelField.get(liveAppPlugin);
-
-        //get callback id from live callbackcontext
-        String liveCallbackId = (liveCallbackContext != null) ? 
liveCallbackContext.getCallbackId() : null;
-        //get callback id from cached callbackcontext
-        String cachedCallbackId = (cachedCallbackContext != null) ? 
cachedCallbackContext.getCallbackId() : null;
-
-        //verify that the live message channel has been initialized
-        assertNotNull(liveCallbackId);
-        //verify that the cached message channel and the live message channel 
have the same id
-        assertEquals(liveCallbackId, cachedCallbackId);
     }
 
-    private void sleep(int timeout) {
-        try {
-            Thread.sleep(timeout);
-        } catch (InterruptedException e) {
-            fail("Unexpected Timeout");
-        }
+    @Test
+    public void testThatCachedCallbackIdIsValid() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra("startUrl", START_URL);
+        TestActivity activity = activityRule.launchActivity(intent);
+        awaitPageFinished(activity, START_URL);
+        awaitJavascript(activityRule, activity, "window.messageChannelReady 
=== true");
+
+        sendTestEvent(activity);
+        awaitJavascript(activityRule, activity, "window.testEventCount === 1");
+
+        // loadUrl recreates the plugins. The new document must receive events 
via
+        // its new channel, not the cached callback belonging to the old 
document.
+        activityRule.runOnUiThread(() -> 
activity.getWebInterface().loadUrl(START_URL));
+        awaitPageFinished(activity, START_URL);
+        awaitJavascript(activityRule, activity,
+                "window.messageChannelReady === true && window.testEventCount 
=== 0");
+        sendTestEvent(activity);
+        awaitJavascript(activityRule, activity, "window.testEventCount === 1");
     }
 }
diff --git 
a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/WebViewTestUtils.java
 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/WebViewTestUtils.java
new file mode 100644
index 00000000..aaeec779
--- /dev/null
+++ 
b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/WebViewTestUtils.java
@@ -0,0 +1,104 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+
+package org.apache.cordova.unittests;
+
+import android.os.SystemClock;
+import android.webkit.WebView;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.espresso.action.GeneralClickAction;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
+import org.json.JSONArray;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+final class WebViewTestUtils {
+    private static final long TIMEOUT_MS = 10000;
+
+    private WebViewTestUtils() {}
+
+    static void awaitPageFinished(TestActivity activity, String expectedUrl) 
throws InterruptedException {
+        long deadline = SystemClock.uptimeMillis() + TIMEOUT_MS;
+        List<String> observed = new ArrayList<>();
+        while (SystemClock.uptimeMillis() < deadline) {
+            String url = activity.onPageFinishedUrl.poll(
+                    Math.max(1, deadline - SystemClock.uptimeMillis()), 
TimeUnit.MILLISECONDS);
+            if (expectedUrl.equals(url)) {
+                return;
+            }
+            if (url == null) {
+                break;
+            }
+            observed.add(url);
+        }
+        fail("Timed out waiting for page " + expectedUrl + "; finished URLs: " 
+ observed);
+    }
+
+    static String evaluateJavascript(ActivityTestRule<?> rule, TestActivity 
activity,
+            String expression) throws Throwable {
+        ArrayBlockingQueue<String> result = new ArrayBlockingQueue<>(1);
+        rule.runOnUiThread(() -> ((WebView) 
activity.getWebInterface().getView())
+                .evaluateJavascript(expression, result::offer));
+        String value = result.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertNotNull("JavaScript evaluation timed out: " + expression, value);
+        return value;
+    }
+
+    static void clickElement(ActivityTestRule<?> rule, TestActivity activity, 
String id) throws Throwable {
+        JSONArray position = new JSONArray(evaluateJavascript(rule, activity,
+                "(function () { var element = document.getElementById('" + id 
+ "');"
+                + "element.scrollIntoView(); var rect = 
element.getBoundingClientRect();"
+                + "return [(rect.left + rect.right) / 2, (rect.top + 
rect.bottom) / 2, innerWidth]; })()"));
+        float x = (float) position.getDouble(0);
+        float y = (float) position.getDouble(1);
+        float viewportWidth = (float) position.getDouble(2);
+        // DOM-generated clicks do not activate a document. Chromium can skip 
those
+        // history entries during Back, so navigation tests need a real touch 
gesture.
+        onView(sameInstance(activity.getWebInterface().getView())).perform(new 
GeneralClickAction(
+                Tap.SINGLE, view -> {
+                    int[] location = new int[2];
+                    view.getLocationOnScreen(location);
+                    float scale = view.getWidth() / viewportWidth;
+                    return new float[] {location[0] + x * scale, location[1] + 
y * scale};
+                }, Press.FINGER));
+    }
+
+    static void awaitJavascript(ActivityTestRule<?> rule, TestActivity 
activity,
+            String condition) throws Throwable {
+        long deadline = SystemClock.uptimeMillis() + TIMEOUT_MS;
+        String value = null;
+        while (SystemClock.uptimeMillis() < deadline) {
+            value = evaluateJavascript(rule, activity, condition);
+            if ("true".equals(value)) {
+                return;
+            }
+            SystemClock.sleep(20);
+        }
+        fail("Timed out waiting for JavaScript condition: " + condition + "; 
last value: " + value);
+    }
+}
diff --git 
a/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html 
b/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html
index 05cfa459..85c6ea01 100755
--- a/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html
+++ b/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html
@@ -35,6 +35,6 @@
         Go to next page.<br>
         If returning from previous page, press "backbutton".  You should exit 
this app.
     </div>
-    <a href="sample2.html" class="btn large">Next page</a>
+    <a id="next_page" href="sample2.html" class="btn large">Next page</a>
 </body> 
 </html>
diff --git 
a/test/androidx/app/src/main/assets/www/backbuttonmultipage/sample2.html 
b/test/androidx/app/src/main/assets/www/backbuttonmultipage/sample2.html
index 26fccc03..b5357a36 100755
--- a/test/androidx/app/src/main/assets/www/backbuttonmultipage/sample2.html
+++ b/test/androidx/app/src/main/assets/www/backbuttonmultipage/sample2.html
@@ -35,6 +35,6 @@
         Go to next page.<br>
         If returning from previous page, press "backbutton".  You should go to 
Page 1.
     </div>
-    <a href="sample3.html" class="btn large">Next page</a>
+    <a id="next_page" href="sample3.html" class="btn large">Next page</a>
 </body> 
 </html>
diff --git a/test/androidx/app/src/main/assets/www/htmlnotfound/index.html 
b/test/androidx/app/src/main/assets/www/htmlnotfound/index.html
new file mode 100644
index 00000000..b480ad86
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/htmlnotfound/index.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<html><head><meta charset="utf-8"><title>Error navigation 
test</title></head><body>Start page for load-error fallback 
regression</body></html>
diff --git a/test/androidx/app/src/main/assets/www/iframe/frame1.html 
b/test/androidx/app/src/main/assets/www/iframe/frame1.html
new file mode 100644
index 00000000..3881436f
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/iframe/frame1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<html><head><meta charset="utf-8"><title>First frame 
page</title></head><body>First frame page</body></html>
diff --git a/test/androidx/app/src/main/assets/www/iframe/index.html 
b/test/androidx/app/src/main/assets/www/iframe/index.html
index 0518940a..4df552b5 100755
--- a/test/androidx/app/src/main/assets/www/iframe/index.html
+++ b/test/androidx/app/src/main/assets/www/iframe/index.html
@@ -1,6 +1,6 @@
-<!DOCTYPE HTML>
+<!DOCTYPE html>
 <!--
-    Licensed to the Apache Software Foundation (ASF) under one
+Licensed to the Apache Software Foundation (ASF) under one
     or more contributor license agreements.  See the NOTICE file
     distributed with this work for additional information
     regarding copyright ownership.  The ASF licenses this file
@@ -18,32 +18,10 @@
     under the License.
 -->
 <html>
-  <head>
-    <meta name="viewport" content="width=320, user-scalable=no" />
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
-    <title>Cordova Tests</title>
-      <link rel="stylesheet" href="../master.css" type="text/css" 
media="screen" title="no title">
-      <script type="text/javascript" charset="utf-8" 
src="../cordova.js"></script>
-      <script type="text/javascript" charset="utf-8" src="../main.js"></script>
-      <script>
-      function loadUrl(url) {
-         document.getElementById('iframe').src = url;
-      }</script>
-  </head>
-  <body onload="init();" id="stage" class="theme">
-    <h1>IFrame</h1>
-    <div id="info">
-        <h4>Cordova: <span id="cordova"> &nbsp;</span></h4>
-        <h4>Deviceready: <span id="deviceready"> &nbsp;</span></h4>
-    </div>
-    <div id="info">
-    Press the two buttons:<br>
-    1. Google Maps should display in iframe.<br>
-    2. Page 2 replaces current page.<br>
-    (NOTE: THIS BEHAVIOR IS WRONG - AND NEEDS TO BE FIXED IN FUTURE RELEASE.)
-    </div>
-    <iframe id="iframe" src="" width="90%" height="200px"></iframe>
-    <a id="google_maps" href="javascript:" class="btn large" 
onclick="loadUrl('http://maps.google.com/maps?output=embed');">Google Maps</a>
-    <a id="javascript_load" href="javascript:" class="btn large" 
onclick="loadUrl('index2.html');">Page 2</a>
-  </body>
+<head><meta charset="utf-8"><title>Iframe history regression</title></head>
+<body>
+    <iframe id="iframe" name="contentFrame" src="about:blank" title="History 
test"></iframe>
+    <a id="first_page" href="frame1.html" target="contentFrame">First page</a>
+    <a id="second_page" href="index2.html" target="contentFrame">Second 
page</a>
+</body>
 </html>
diff --git a/test/androidx/app/src/main/assets/www/iframe/index2.html 
b/test/androidx/app/src/main/assets/www/iframe/index2.html
index 799a1115..c924e460 100755
--- a/test/androidx/app/src/main/assets/www/iframe/index2.html
+++ b/test/androidx/app/src/main/assets/www/iframe/index2.html
@@ -1,6 +1,6 @@
-<!DOCTYPE HTML>
+<!DOCTYPE html>
 <!--
-    Licensed to the Apache Software Foundation (ASF) under one
+Licensed to the Apache Software Foundation (ASF) under one
     or more contributor license agreements.  See the NOTICE file
     distributed with this work for additional information
     regarding copyright ownership.  The ASF licenses this file
@@ -17,24 +17,4 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<html>
-  <head>
-    <meta name="viewport" content="width=320, user-scalable=no" />
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
-    <title>Cordova Tests</title>
-      <link rel="stylesheet" href="../master.css" type="text/css" 
media="screen" title="no title">
-      <script type="text/javascript" charset="utf-8" 
src="../cordova.js"></script>
-      <script type="text/javascript" charset="utf-8" src="../main.js"></script>
-  </head>
-  <body onload="init();" id="stage" class="theme">
-    <h1>IFrame window</h1>
-    <div id="info">
-        <h4>Cordova: <span id="cordova"> &nbsp;</span></h4>
-        <h4>Deviceready: <span id="deviceready"> &nbsp;</span></h4>
-     </div>
-     <div id="info">
-     This should display a Cordova page inside an iframe.  The info above 
should be filled out.
-     (NOTE: THIS DOES NOT WORK AND NEEDS TO BE FIXED IN FUTURE RELEASE.)
-     </div>
-  </body>
-</html>
+<html><head><meta charset="utf-8"><title>Second frame 
page</title></head><body>Second frame page</body></html>
diff --git a/test/androidx/app/src/main/assets/www/messagechannel/index.html 
b/test/androidx/app/src/main/assets/www/messagechannel/index.html
new file mode 100644
index 00000000..513fd3de
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/messagechannel/index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Message channel reload regression</title>
+    <script src="../cordova.js"></script>
+    <script>
+        window.testEventCount = 0;
+        document.addEventListener('menubutton', function () { 
window.testEventCount += 1; });
+        document.addEventListener('deviceready', function () { 
window.messageChannelReady = true; });
+    </script>
+</head>
+<body>Message channel reload regression</body>
+</html>
diff --git 
a/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
 
b/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
index 7581a1bc..55cdfd62 100644
--- 
a/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
+++ 
b/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
@@ -34,6 +34,8 @@ import java.util.concurrent.RunnableFuture;
 
 public class TestActivity extends CordovaActivity {
 
+    public final ArrayBlockingQueue<String> onReceivedErrorUrl = new 
ArrayBlockingQueue<String>(500);
+
     public final ArrayBlockingQueue<String> onPageFinishedUrl = new 
ArrayBlockingQueue<String>(500);
 
 
@@ -54,6 +56,12 @@ public class TestActivity extends CordovaActivity {
         loadUrl(launchUrl);
     }
 
+    @Override
+    public void onReceivedError(int errorCode, String description, String 
failingUrl) {
+        onReceivedErrorUrl.offer(failingUrl);
+        super.onReceivedError(errorCode, description, failingUrl);
+    }
+
     @Override
     public Object onMessage(String id, Object data) {
         if ("onPageFinished".equals(id)) {
diff --git a/test/androidx/app/src/main/res/xml/config.xml 
b/test/androidx/app/src/main/res/xml/config.xml
index 6c234de1..19ff31e5 100644
--- a/test/androidx/app/src/main/res/xml/config.xml
+++ b/test/androidx/app/src/main/res/xml/config.xml
@@ -31,7 +31,7 @@
     <access origin="https://*.googleapis.com/*"; />
     <access origin="https://*.gstatic.com/*"; />
     <content src="index.html" />
-    <preference name="errorUrl" 
value="file:///android_asset/www/htmlnotfound/error.html" />
+    <preference name="errorUrl" 
value="https://localhost/htmlnotfound/error.html"; />
     <preference name="loglevel" value="DEBUG" />
     <preference name="useBrowserHistory" value="true" />
     <preference name="exit-on-suspend" value="false" />


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to