Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2360)

Unified Diff: chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java

Issue 11778005: JavascriptAppModalDialog tests and necessary infrastructure. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Upstreamed pliard@'s click-after-dismiss regression test. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a29bd7bde14851d78f895ac7e05a87f1e1606d6
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
@@ -0,0 +1,427 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.test;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.JavascriptAppModalDialog;
+import org.chromium.chrome.browser.TabBase;
+import org.chromium.chrome.browser.test.util.TabBaseUtils;
+import org.chromium.chrome.browser.test.util.TabBaseUtils.TestCallbackHelperContainerForTab;
+import org.chromium.chrome.testshell.ChromiumTestShellTestBase;
+import org.chromium.content.browser.ContentViewClient;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
+import org.chromium.content.browser.test.util.TestContentViewClient;
+import org.chromium.content.browser.test.util.TestContentViewClientWrapper;
+import org.chromium.content.browser.test.util.TestWebContentsObserver;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Test suite for displaying and functioning of modal dialogs.
+ */
+public class ModalDialogTest extends ChromiumTestShellTestBase {
+ private final static String TAG = ModalDialogTest.class.getSimpleName();
+ private final static String EMPTY_PAGE =
+ "data:text/html;utf-8,<!doctype html>" +
+ "<title>Modal Dialog Test</title><p>Testcase.</p>";
+ private final static String BEFORE_UNLOAD_URL =
+ "data:text/html;utf-8,<!doctype html>" +
+ "<head><script>window.onbeforeunload=function() {" +
+ "return 'Are you sure?';" +
+ "};</script></head></body>";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ launchChromiumTestShellWithUrl(EMPTY_PAGE);
+ assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
+ }
+
+ /**
+ * Verifies modal alert-dialog appearance and that JavaScript execution is
+ * able to continue after dismissal.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testAlertModalDialog()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ final OnEvaluateJavaScriptResultHelper scriptEvent =
+ executeJavaScriptAndWaitForDialog("alert('Hello Android!');");
+
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ clickOk(jsDialog);
+ assertTrue("JavaScript execution should continue after closing prompt.",
+ scriptEvent.waitUntilHasValue());
+ }
+
+ /**
+ * Verifies that clicking on a button twice doesn't crash.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testAlertModalDialogWithTwoClicks()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ OnEvaluateJavaScriptResultHelper scriptEvent =
+ executeJavaScriptAndWaitForDialog("alert('Hello Android');");
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ clickOk(jsDialog);
+ clickOk(jsDialog);
+
+ assertTrue("JavaScript execution should continue after closing prompt.",
+ scriptEvent.waitUntilHasValue());
+ }
+
+ /**
+ * Verifies that modal confirm-dialogs display, two buttons are visible and
+ * the return value of [Ok] equals true, [Cancel] equals false.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testConfirmModalDialog()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ OnEvaluateJavaScriptResultHelper scriptEvent =
+ executeJavaScriptAndWaitForDialog("confirm('Android');");
+
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ Button[] buttons = getAlertDialogButtons(jsDialog.getDialogForTest());
+ assertNotNull("No cancel button in confirm dialog.", buttons[0]);
+ assertEquals("Cancel button is not visible.", View.VISIBLE, buttons[0].getVisibility());
+ if (buttons[1] != null) {
+ assertNotSame("Neutral button visible when it should not.",
+ View.VISIBLE, buttons[1].getVisibility());
+ }
+ assertNotNull("No OK button in confirm dialog.", buttons[2]);
+ assertEquals("OK button is not visible.", View.VISIBLE, buttons[2].getVisibility());
+
+ clickOk(jsDialog);
+ assertTrue("JavaScript execution should continue after closing dialog.",
+ scriptEvent.waitUntilHasValue());
+
+ String resultString = scriptEvent.getJsonResultAndClear();
+ assertEquals("Invalid return value.", "true", resultString);
+
+ // Try again, pressing cancel this time.
+ scriptEvent = executeJavaScriptAndWaitForDialog("confirm('Android');");
+ jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ clickCancel(jsDialog);
+ assertTrue("JavaScript execution should continue after closing dialog.",
+ scriptEvent.waitUntilHasValue());
+
+ resultString = scriptEvent.getJsonResultAndClear();
+ assertEquals("Invalid return value.", "false", resultString);
+ }
+
+ /**
+ * Verifies that modal prompt-dialogs display and the result is returned.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testPromptModalDialog()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ final String promptText = "Hello Android!";
+ final OnEvaluateJavaScriptResultHelper scriptEvent =
+ executeJavaScriptAndWaitForDialog("prompt('Android', 'default');");
+
+ final JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ // Set the text in the prompt field of the dialog.
+ boolean result = ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ EditText prompt = (EditText) jsDialog.getDialogForTest().findViewById(
+ org.chromium.chrome.R.id.js_modal_dialog_prompt);
+ if (prompt == null) return false;
+ prompt.setText(promptText);
+ return true;
+ }
+ });
+ assertTrue("Failed to find prompt view in prompt dialog.", result);
+
+ clickOk(jsDialog);
+ assertTrue("JavaScript execution should continue after closing prompt.",
+ scriptEvent.waitUntilHasValue());
+
+ String resultString = scriptEvent.getJsonResultAndClear();
+ assertEquals("Invalid return value.", '"' + promptText + '"', resultString);
+ }
+
+ /**
+ * Verifies beforeunload dialogs are shown and they block/allow navigation
+ * as appropriate.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testBeforeUnloadDialog()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ loadUrlWithSanitization(BEFORE_UNLOAD_URL);
+ executeJavaScriptAndWaitForDialog("history.back();");
+
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+ checkButtonPresenceVisibilityText(
+ jsDialog, 0, org.chromium.content.R.string.stay_on_this_page,
+ "Stay on this page");
+ clickCancel(jsDialog);
+
+ assertEquals(BEFORE_UNLOAD_URL, getActivity().getActiveContentView().getUrl());
+ executeJavaScriptAndWaitForDialog("history.back();");
+
+ jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+ checkButtonPresenceVisibilityText(
+ jsDialog, 2, org.chromium.content.R.string.leave_this_page,
+ "Leave this page");
+
+ final TestCallbackHelperContainer.OnPageFinishedHelper onPageLoaded =
+ getActiveTabTestCallbackHelperContainer().getOnPageFinishedHelper();
+ int callCount = onPageLoaded.getCallCount();
+ clickOk(jsDialog);
+ onPageLoaded.waitForCallback(callCount);
+ assertEquals(EMPTY_PAGE, getActivity().getActiveContentView().getUrl());
+ }
+
+ /**
+ * Verifies that when showing a beforeunload dialogs as a result of a page
+ * reload, the correct UI strings are used.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testBeforeUnloadOnReloadDialog()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ loadUrlWithSanitization(BEFORE_UNLOAD_URL);
+ executeJavaScriptAndWaitForDialog("window.location.reload();");
+
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ checkButtonPresenceVisibilityText(
+ jsDialog, 0, org.chromium.content.R.string.dont_reload_this_page,
+ "Don't reload this page");
+ checkButtonPresenceVisibilityText(
+ jsDialog, 2, org.chromium.content.R.string.reload_this_page,
+ "Reload this page");
+ }
+
+ /**
+ * Verifies that repeated dialogs give the option to disable dialogs
+ * altogether and then that disabling them works.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testDisableRepeatedDialogs()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ OnEvaluateJavaScriptResultHelper scriptEvent =
+ executeJavaScriptAndWaitForDialog("alert('Android');");
+
+ // Show a dialog once.
+ JavascriptAppModalDialog jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+
+ clickCancel(jsDialog);
+ scriptEvent.waitUntilHasValue();
+
+ // Show it again, it should have the option to suppress subsequent dialogs.
+ scriptEvent = executeJavaScriptAndWaitForDialog("alert('Android');");
+ jsDialog = getCurrentDialog();
+ assertNotNull("No dialog showing.", jsDialog);
+ final AlertDialog dialog = jsDialog.getDialogForTest();
+ String errorMessage = ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
+ @Override
+ public String call() {
+ final CheckBox suppress = (CheckBox) dialog.findViewById(
+ org.chromium.chrome.R.id.suppress_js_modal_dialogs);
+ if (suppress == null) return "Suppress checkbox not found.";
+ if (suppress.getVisibility() != View.VISIBLE) {
+ return "Suppress checkbox is not visible.";
+ }
+ suppress.setChecked(true);
+ return null;
+ }
+ });
+ assertNull(errorMessage, errorMessage);
+ clickCancel(jsDialog);
+ scriptEvent.waitUntilHasValue();
+
+ scriptEvent.evaluateJavaScript(getActivity().getActiveContentView().getContentViewCore(),
+ "alert('Android');");
+ assertTrue("No further dialog boxes should be shown.", scriptEvent.waitUntilHasValue());
+ }
+
+ /**
+ * Displays a dialog and closes the tab in the background before attempting
+ * to accept the dialog. Verifies that the dialog is dismissed when the tab
+ * is closed.
+ */
+ @MediumTest
+ @Feature({"Browser", "Main"})
+ public void testDialogDismissedAfterClosingTab()
+ throws InterruptedException, TimeoutException, ExecutionException {
+ executeJavaScriptAndWaitForDialog("alert('Android')");
+
+ final TestCallbackHelperContainerForTab.OnCloseTabHelper onTabClosed =
+ getActiveTabTestCallbackHelperContainer().getOnCloseTabHelper();
+ int callCount = onTabClosed.getCallCount();
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().createTab(EMPTY_PAGE);
+ }
+ });
+ onTabClosed.waitForCallback(callCount);
+
+ // Closing the tab should have dismissed the dialog.
+ boolean criteriaSatisfied = CriteriaHelper.pollForCriteria(
+ new JavascriptAppModalDialogShownCriteria(false));
+ assertTrue("The dialog should have been dismissed when its tab was closed.",
+ criteriaSatisfied);
+ }
+
+ /**
+ * Asynchronously executes the given code for spawning a dialog and waits
+ * for the dialog to be visible.
+ */
+ private OnEvaluateJavaScriptResultHelper executeJavaScriptAndWaitForDialog(String script)
+ throws InterruptedException {
+ final OnEvaluateJavaScriptResultHelper helper =
+ getActiveTabTestCallbackHelperContainer().getOnEvaluateJavaScriptResultHelper();
+ return executeJavaScriptAndWaitForDialog(helper, script);
+ }
+
+ /**
+ * Given a JavaScript evaluation helper, asynchronously executes the given
+ * code for spawning a dialog and waits for the dialog to be visible.
+ */
+ private OnEvaluateJavaScriptResultHelper executeJavaScriptAndWaitForDialog(
+ final OnEvaluateJavaScriptResultHelper helper, String script)
+ throws InterruptedException {
+ helper.evaluateJavaScript(getActivity().getActiveContentView().getContentViewCore(),
+ script);
+ boolean criteriaSatisfied = CriteriaHelper.pollForCriteria(
+ new JavascriptAppModalDialogShownCriteria(true));
+ assertTrue("Could not spawn or locate a modal dialog.", criteriaSatisfied);
+ return helper;
+ }
+
+ /**
+ * Returns an array of the 3 buttons for this dialog, in the order
+ * BUTTON_NEGATIVE, BUTTON_NEUTRAL and BUTTON_POSITIVE. Any of these values
+ * can be null.
+ */
+ private Button[] getAlertDialogButtons(final AlertDialog dialog) throws ExecutionException {
+ return ThreadUtils.runOnUiThreadBlocking(new Callable<Button[]>() {
+ @Override
+ public Button[] call() {
+ final Button[] buttons = new Button[3];
+ buttons[0] = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+ buttons[1] = dialog.getButton(DialogInterface.BUTTON_NEUTRAL);
+ buttons[2] = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ return buttons;
+ }
+ });
+ }
+
+ /**
+ * Returns the current JavaScript modal dialog showing or null if no such dialog is currently
+ * showing.
+ */
+ private JavascriptAppModalDialog getCurrentDialog() throws ExecutionException {
+ return ThreadUtils.runOnUiThreadBlocking(new Callable<JavascriptAppModalDialog>() {
+ @Override
+ public JavascriptAppModalDialog call() {
+ return JavascriptAppModalDialog.getCurrentDialogForTest();
+ }
+ });
+ }
+
+ private static class JavascriptAppModalDialogShownCriteria implements Criteria {
+ private final boolean mShouldBeShown;
+
+ public JavascriptAppModalDialogShownCriteria(boolean shouldBeShown) {
+ mShouldBeShown = shouldBeShown;
+ }
+
+ @Override
+ public boolean isSatisfied() {
+ try {
+ return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ final boolean isShown =
+ JavascriptAppModalDialog.getCurrentDialogForTest() != null;
+ return mShouldBeShown == isShown;
+ }
+ });
+ } catch (ExecutionException e) {
+ Log.e(TAG, "Failed to getCurrentDialog", e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Simulates pressing the OK button of the passed dialog.
+ */
+ private void clickOk(JavascriptAppModalDialog dialog) {
+ clickButton(dialog, DialogInterface.BUTTON_POSITIVE);
+ }
+
+ /**
+ * Simulates pressing the Cancel button of the passed dialog.
+ */
+ private void clickCancel(JavascriptAppModalDialog dialog) {
+ clickButton(dialog, DialogInterface.BUTTON_NEGATIVE);
+ }
+
+ private void clickButton(final JavascriptAppModalDialog dialog, final int whichButton) {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ dialog.onClick(null, whichButton);
+ }
+ });
+ }
+
+ private void checkButtonPresenceVisibilityText(
+ JavascriptAppModalDialog jsDialog, int buttonIndex,
+ int expectedTextResourceId, String readableName) throws ExecutionException {
+ final Button[] buttons = getAlertDialogButtons(jsDialog.getDialogForTest());
+ final Button button = buttons[buttonIndex];
+ assertNotNull("No '" + readableName + "' button in confirm dialog.", button);
+ assertEquals("'" + readableName + "' button is not visible.",
+ View.VISIBLE,
+ button.getVisibility());
+ assertEquals("'" + readableName + "' button has wrong text",
+ getActivity().getResources().getString(expectedTextResourceId),
+ button.getText().toString());
+ }
+
+ private TestCallbackHelperContainerForTab getActiveTabTestCallbackHelperContainer() {
+ return TabBaseUtils.getTestCallbackHelperContainer(getActivity().getActiveTab());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698