Skip to content

Commit

Permalink
[wpe] Add prompt dialog and instrumentation tests
Browse files Browse the repository at this point in the history
Add rest of the dialogs and ensure their behavior matches
Android system webview by implementing instrumentation tests to
guarantee correct results

Patch also introduces bunch of instrumentation test helper classes
and functions
  • Loading branch information
zhani committed Aug 25, 2024
1 parent 18c568d commit a6c8983
Show file tree
Hide file tree
Showing 14 changed files with 583 additions and 102 deletions.
58 changes: 58 additions & 0 deletions wpe/src/androidTest/java/com/wpe/wpeview/TestWPEViewClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.wpe.wpeview;

import android.util.Log;

import androidx.annotation.NonNull;

import com.wpe.wpeview.util.CallbackHelper;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class TestWPEViewClient implements WPEViewClient {

private final CallbackHelper onPageFinishedHelper;
private final OnEvaluateJavaScriptResultHelper onEvaluateJavascriptResultHelper;

public TestWPEViewClient() {
onPageFinishedHelper = new CallbackHelper();
onEvaluateJavascriptResultHelper = new OnEvaluateJavaScriptResultHelper();
}

CallbackHelper getOnPageFinishedHelper() { return onPageFinishedHelper; }
OnEvaluateJavaScriptResultHelper getOnEvaluateJavascriptResultHelper() { return onEvaluateJavascriptResultHelper; }

public static class OnEvaluateJavaScriptResultHelper extends CallbackHelper {
private String result;
public void evaluateJavascript(WPEView view, String script) {
view.evaluateJavascript(script, this::notifyCalled);
}

public boolean hasValue() { return result != null; }

public String getResultAndClear() {
assert hasValue();
String res = result;
result = null;
return res;
}

public boolean waitUntilHasValue(long timeout, TimeUnit unit) throws TimeoutException {
// Reads and writes are atomic for reference variables in java, this is thread safe
if (hasValue())
return true;
waitForCallback(timeout, unit);
return hasValue();
}

private void notifyCalled(String result) {
this.result = result;
notifyCalled();
}
}

@Override
public void onPageFinished(@NonNull WPEView view, @NonNull String url) {
onPageFinishedHelper.notifyCalled();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.wpe.wpeview;

import androidx.test.core.app.ActivityScenario;

import org.junit.Assert;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class WPEViewActivityScenarioHelper {
public static void loadHtmlSync(ActivityScenario<WPEViewTestActivity> scenario, TestWPEViewClient wpeViewClient,
String html) {
scenario.onActivity(activity -> {
activity.getWPEView().setWPEViewClient(wpeViewClient);
activity.getWPEView().loadHtml(html, null);
});
wpeViewClient.getOnPageFinishedHelper().waitForCallback(2, TimeUnit.SECONDS);
}

public static String evaluateJavaScriptAndWaitForResult(ActivityScenario<WPEViewTestActivity> scenario,
TestWPEViewClient wpeViewClient, String script)
throws TimeoutException {
TestWPEViewClient.OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper =
wpeViewClient.getOnEvaluateJavascriptResultHelper();
scenario.onActivity(
activity -> { onEvaluateJavaScriptResultHelper.evaluateJavascript(activity.getWPEView(), script); });
onEvaluateJavaScriptResultHelper.waitUntilHasValue(5, TimeUnit.SECONDS);
Assert.assertTrue("Failed to retrieve JavaScript evaluation results.",
onEvaluateJavaScriptResultHelper.hasValue());
return wpeViewClient.getOnEvaluateJavascriptResultHelper().getResultAndClear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package com.wpe.wpeview;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.runner.AndroidJUnit4;

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

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

@RunWith(AndroidJUnit4.class)
public class WPEViewDialogOverrideTest {
private static final String EMPTY_PAGE = "<!doctype html>"
+ "<title>Dialog Test</title><p>Testcase.</p>";

@Rule
public ActivityScenarioRule<WPEViewTestActivity> wpeViewActivityTestRule =
new ActivityScenarioRule<>(WPEViewTestActivity.class);

@Test
public void testOverrideAlertHandling() throws Throwable {
final String alertText = "Hello World!";

final AtomicBoolean callbackCalled = new AtomicBoolean(false);

TestWPEViewClient wpeViewClient = new TestWPEViewClient();
ActivityScenario<WPEViewTestActivity> scenario = wpeViewActivityTestRule.getScenario();
scenario
.onActivity(activity -> {
activity.getWPEView().setWPEChromeClient(new WPEChromeClient() {
@Override
public boolean onJsAlert(@NonNull WPEView view, @NonNull String url, @NonNull String message,
@NonNull WPEJsResult result) {
callbackCalled.set(true);
result.confirm();
return true;
}
});
})
.moveToState(Lifecycle.State.RESUMED);

WPEViewActivityScenarioHelper.loadHtmlSync(scenario, wpeViewClient, EMPTY_PAGE);
WPEViewActivityScenarioHelper.evaluateJavaScriptAndWaitForResult(scenario, wpeViewClient,
"alert('" + alertText + "')");

Assert.assertTrue(callbackCalled.get());
}

@Test
public void testOverrideConfirmHandlingConfirmed() throws Throwable {
final String confirmText = "Would you like to confirm?";

final AtomicBoolean callbackCalled = new AtomicBoolean(false);

TestWPEViewClient wpeViewClient = new TestWPEViewClient();
ActivityScenario<WPEViewTestActivity> scenario = wpeViewActivityTestRule.getScenario();
scenario
.onActivity(activity -> {
activity.getWPEView().setWPEChromeClient(new WPEChromeClient() {
@Override
public boolean onJsConfirm(@NonNull WPEView view, @NonNull String url, @NonNull String message,
@NonNull WPEJsResult result) {
callbackCalled.set(true);
result.confirm();
return true;
}
});
activity.getWPEView().loadHtml(EMPTY_PAGE, null);
})
.moveToState(Lifecycle.State.RESUMED);

WPEViewActivityScenarioHelper.loadHtmlSync(scenario, wpeViewClient, EMPTY_PAGE);
String result = WPEViewActivityScenarioHelper.evaluateJavaScriptAndWaitForResult(
scenario, wpeViewClient, "confirm('" + confirmText + "')");

Assert.assertTrue(callbackCalled.get());
Assert.assertEquals("true", result);
}

@Test
public void testOverrideConfirmHandlingCancelled() throws Throwable {
final String confirmText = "Would you like to confirm?";

final AtomicBoolean callbackCalled = new AtomicBoolean(false);

TestWPEViewClient wpeViewClient = new TestWPEViewClient();
ActivityScenario<WPEViewTestActivity> scenario = wpeViewActivityTestRule.getScenario();
scenario
.onActivity(activity -> {
activity.getWPEView().setWPEChromeClient(new WPEChromeClient() {
@Override
public boolean onJsConfirm(@NonNull WPEView view, @NonNull String url, @NonNull String message,
@NonNull WPEJsResult result) {
callbackCalled.set(true);
result.cancel();
return true;
}
});
activity.getWPEView().loadHtml(EMPTY_PAGE, null);
})
.moveToState(Lifecycle.State.RESUMED);

WPEViewActivityScenarioHelper.loadHtmlSync(scenario, wpeViewClient, EMPTY_PAGE);
String result = WPEViewActivityScenarioHelper.evaluateJavaScriptAndWaitForResult(
scenario, wpeViewClient, "confirm('" + confirmText + "')");

Assert.assertTrue(callbackCalled.get());
Assert.assertEquals("false", result);
}

@Test
public void testOverridePromptHandling() throws Throwable {
final String promptText = "How is the weather today?";
final String promptDefault = "It's sunny and warm today.";
final String promptResult = "It’s so hot.";

final AtomicBoolean callbackCalled = new AtomicBoolean(false);

TestWPEViewClient wpeViewClient = new TestWPEViewClient();
ActivityScenario<WPEViewTestActivity> scenario = wpeViewActivityTestRule.getScenario();
scenario
.onActivity(activity -> {
activity.getWPEView().setWPEChromeClient(new WPEChromeClient() {
@Override
public boolean onJsPrompt(@NonNull WPEView view, @NonNull String url, @NonNull String message,
@NonNull String defaultValue, @NonNull WPEJsPromptResult result) {
Assert.assertEquals(promptText, message);
Assert.assertEquals(promptDefault, defaultValue);

result.confirm(promptResult);
callbackCalled.set(true);
return true;
}
});
})
.moveToState(Lifecycle.State.RESUMED);
WPEViewActivityScenarioHelper.loadHtmlSync(scenario, wpeViewClient, EMPTY_PAGE);
String result = WPEViewActivityScenarioHelper.evaluateJavaScriptAndWaitForResult(
scenario, wpeViewClient, "prompt(\"" + promptText + "\", \"" + promptDefault + "\")");
Assert.assertTrue(callbackCalled.get());
Assert.assertEquals("\"" + promptResult + "\"", result);
}
}
49 changes: 22 additions & 27 deletions wpe/src/androidTest/java/com/wpe/wpeview/WPEViewImeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,34 +32,29 @@ public class WPEViewImeTest {
public void testIMEVisible() {
CountDownLatch latch = new CountDownLatch(1);
ActivityScenario<WPEViewTestActivity> scenario = wpeViewActivityTestRule.getScenario();
scenario.onActivity(activity -> {
activity.getWPEView().setWPEViewClient(new WPEViewClient() {
@Override
public void onPageFinished(@NonNull WPEView view, @NonNull String url) {
String focusScript = "function onDocumentFocused() {\n"
+ " document.getElementById('editor').focus();\n"
+ " test.onEditorFocused();\n"
+ "}\n"
+ "(function() {\n"
+ "if (document.hasFocus()) {\n"
+ " onDocumentFocused();"
+ "} else {\n"
+ " window.addEventListener('focus', onDocumentFocused);\n"
+ "}})();";
view.evaluateJavascript(focusScript, new WPECallback<String>() {
@Override
public void onResult(String value) {
latch.countDown();
}
});
}
});

String htmlDocument = "<html><body contenteditable id='editor'></body></html>";
activity.getWPEView().loadHtml(htmlDocument, null);
});
scenario
.onActivity(activity -> {
activity.getWPEView().setWPEViewClient(new WPEViewClient() {
@Override
public void onPageFinished(@NonNull WPEView view, @NonNull String url) {
String focusScript = "function onDocumentFocused() {\n"
+ " document.getElementById('editor').focus();\n"
+ " test.onEditorFocused();\n"
+ "}\n"
+ "(function() {\n"
+ "if (document.hasFocus()) {\n"
+ " onDocumentFocused();"
+ "} else {\n"
+ " window.addEventListener('focus', onDocumentFocused);\n"
+ "}})();";
view.evaluateJavascript(focusScript, value -> latch.countDown());
}
});

scenario.moveToState(Lifecycle.State.RESUMED);
String htmlDocument = "<html><body contenteditable id='editor'></body></html>";
activity.getWPEView().loadHtml(htmlDocument, null);
})
.moveToState(Lifecycle.State.RESUMED);

try {
latch.await(10, TimeUnit.SECONDS);
Expand Down
Loading

0 comments on commit a6c8983

Please sign in to comment.