Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / android / javatests / src / org / chromium / chrome / browser / test / ModalDialogTest.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.test;
6
7 import android.app.AlertDialog;
8 import android.content.DialogInterface;
9 import android.test.suitebuilder.annotation.MediumTest;
10 import android.util.Log;
11 import android.view.View;
12 import android.widget.Button;
13 import android.widget.CheckBox;
14 import android.widget.EditText;
15
16 import org.chromium.base.ThreadUtils;
17 import org.chromium.base.test.util.DisabledTest;
18 import org.chromium.base.test.util.Feature;
19 import org.chromium.base.test.util.UrlUtils;
20 import org.chromium.chrome.browser.JavascriptAppModalDialog;
21 import org.chromium.chrome.shell.ChromeShellTestBase;
22 import org.chromium.content.browser.test.util.Criteria;
23 import org.chromium.content.browser.test.util.CriteriaHelper;
24 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
25 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper;
26
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.TimeoutException;
30
31 /**
32  * Test suite for displaying and functioning of modal dialogs.
33  */
34 public class ModalDialogTest extends ChromeShellTestBase {
35     private static final String TAG = "ModalDialogTest";
36     private static final String EMPTY_PAGE = UrlUtils.encodeHtmlDataUri(
37             "<html><title>Modal Dialog Test</title><p>Testcase.</p></title></html>");
38     private static final String BEFORE_UNLOAD_URL = UrlUtils.encodeHtmlDataUri(
39             "<html>" +
40             "<head><script>window.onbeforeunload=function() {" +
41             "return 'Are you sure?';" +
42             "};</script></head></html>");
43
44     @Override
45     public void setUp() throws Exception {
46         super.setUp();
47         launchChromeShellWithUrl(EMPTY_PAGE);
48         assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
49     }
50
51     /**
52      * Verifies modal alert-dialog appearance and that JavaScript execution is
53      * able to continue after dismissal.
54      */
55     @MediumTest
56     @Feature({"Browser", "Main"})
57     public void testAlertModalDialog()
58             throws InterruptedException, TimeoutException, ExecutionException {
59         final OnEvaluateJavaScriptResultHelper scriptEvent =
60                 executeJavaScriptAndWaitForDialog("alert('Hello Android!');");
61
62         JavascriptAppModalDialog jsDialog = getCurrentDialog();
63         assertNotNull("No dialog showing.", jsDialog);
64
65         clickOk(jsDialog);
66         assertTrue("JavaScript execution should continue after closing prompt.",
67                 scriptEvent.waitUntilHasValue());
68     }
69
70     /**
71      * Verifies that clicking on a button twice doesn't crash.
72      */
73     @MediumTest
74     @Feature({"Browser", "Main"})
75     public void testAlertModalDialogWithTwoClicks()
76             throws InterruptedException, TimeoutException, ExecutionException {
77         OnEvaluateJavaScriptResultHelper scriptEvent =
78                 executeJavaScriptAndWaitForDialog("alert('Hello Android');");
79         JavascriptAppModalDialog jsDialog = getCurrentDialog();
80         assertNotNull("No dialog showing.", jsDialog);
81
82         clickOk(jsDialog);
83         clickOk(jsDialog);
84
85         assertTrue("JavaScript execution should continue after closing prompt.",
86                 scriptEvent.waitUntilHasValue());
87     }
88
89     /**
90      * Verifies that modal confirm-dialogs display, two buttons are visible and
91      * the return value of [Ok] equals true, [Cancel] equals false.
92      */
93     @MediumTest
94     @Feature({"Browser", "Main"})
95     public void testConfirmModalDialog()
96             throws InterruptedException, TimeoutException, ExecutionException {
97         OnEvaluateJavaScriptResultHelper scriptEvent =
98                 executeJavaScriptAndWaitForDialog("confirm('Android');");
99
100         JavascriptAppModalDialog jsDialog = getCurrentDialog();
101         assertNotNull("No dialog showing.", jsDialog);
102
103         Button[] buttons = getAlertDialogButtons(jsDialog.getDialogForTest());
104         assertNotNull("No cancel button in confirm dialog.", buttons[0]);
105         assertEquals("Cancel button is not visible.", View.VISIBLE, buttons[0].getVisibility());
106         if (buttons[1] != null) {
107             assertNotSame("Neutral button visible when it should not.",
108                     View.VISIBLE, buttons[1].getVisibility());
109         }
110         assertNotNull("No OK button in confirm dialog.", buttons[2]);
111         assertEquals("OK button is not visible.", View.VISIBLE, buttons[2].getVisibility());
112
113         clickOk(jsDialog);
114         assertTrue("JavaScript execution should continue after closing dialog.",
115                 scriptEvent.waitUntilHasValue());
116
117         String resultString = scriptEvent.getJsonResultAndClear();
118         assertEquals("Invalid return value.", "true", resultString);
119
120         // Try again, pressing cancel this time.
121         scriptEvent = executeJavaScriptAndWaitForDialog("confirm('Android');");
122         jsDialog = getCurrentDialog();
123         assertNotNull("No dialog showing.", jsDialog);
124
125         clickCancel(jsDialog);
126         assertTrue("JavaScript execution should continue after closing dialog.",
127                 scriptEvent.waitUntilHasValue());
128
129         resultString = scriptEvent.getJsonResultAndClear();
130         assertEquals("Invalid return value.", "false", resultString);
131     }
132
133     /**
134      * Verifies that modal prompt-dialogs display and the result is returned.
135      */
136     @MediumTest
137     @Feature({"Browser", "Main"})
138     public void testPromptModalDialog()
139             throws InterruptedException, TimeoutException, ExecutionException {
140         final String promptText = "Hello Android!";
141         final OnEvaluateJavaScriptResultHelper scriptEvent =
142                 executeJavaScriptAndWaitForDialog("prompt('Android', 'default');");
143
144         final JavascriptAppModalDialog jsDialog = getCurrentDialog();
145         assertNotNull("No dialog showing.", jsDialog);
146
147         // Set the text in the prompt field of the dialog.
148         boolean result = ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
149             @Override
150             public Boolean call() {
151                 EditText prompt = (EditText) jsDialog.getDialogForTest().findViewById(
152                         org.chromium.chrome.R.id.js_modal_dialog_prompt);
153                 if (prompt == null) return false;
154                 prompt.setText(promptText);
155                 return true;
156             }
157         });
158         assertTrue("Failed to find prompt view in prompt dialog.", result);
159
160         clickOk(jsDialog);
161         assertTrue("JavaScript execution should continue after closing prompt.",
162                 scriptEvent.waitUntilHasValue());
163
164         String resultString = scriptEvent.getJsonResultAndClear();
165         assertEquals("Invalid return value.", '"' + promptText + '"', resultString);
166     }
167
168     /**
169      * Verifies beforeunload dialogs are shown and they block/allow navigation
170      * as appropriate.
171      */
172     //@MediumTest
173     //@Feature({"Browser", "Main"})
174     @DisabledTest //crbug/270593
175     public void testBeforeUnloadDialog()
176             throws InterruptedException, TimeoutException, ExecutionException {
177         loadUrlWithSanitization(BEFORE_UNLOAD_URL);
178         executeJavaScriptAndWaitForDialog("history.back();");
179
180         JavascriptAppModalDialog jsDialog = getCurrentDialog();
181         assertNotNull("No dialog showing.", jsDialog);
182         checkButtonPresenceVisibilityText(
183                 jsDialog, 0, org.chromium.chrome.R.string.stay_on_this_page,
184                 "Stay on this page");
185         clickCancel(jsDialog);
186
187         assertEquals(BEFORE_UNLOAD_URL, getActivity().getActiveContentViewCore()
188                 .getWebContents().getUrl());
189         executeJavaScriptAndWaitForDialog("history.back();");
190
191         jsDialog = getCurrentDialog();
192         assertNotNull("No dialog showing.", jsDialog);
193         checkButtonPresenceVisibilityText(
194                 jsDialog, 2, org.chromium.chrome.R.string.leave_this_page,
195                 "Leave this page");
196
197         final TestCallbackHelperContainer.OnPageFinishedHelper onPageLoaded =
198                 getActiveTabTestCallbackHelperContainer().getOnPageFinishedHelper();
199         int callCount = onPageLoaded.getCallCount();
200         clickOk(jsDialog);
201         onPageLoaded.waitForCallback(callCount);
202         assertEquals(EMPTY_PAGE, getActivity().getActiveContentViewCore()
203                 .getWebContents().getUrl());
204     }
205
206     /**
207      * Verifies that when showing a beforeunload dialogs as a result of a page
208      * reload, the correct UI strings are used.
209      */
210     @MediumTest
211     @Feature({"Browser", "Main"})
212     public void testBeforeUnloadOnReloadDialog()
213             throws InterruptedException, TimeoutException, ExecutionException {
214         loadUrlWithSanitization(BEFORE_UNLOAD_URL);
215         executeJavaScriptAndWaitForDialog("window.location.reload();");
216
217         JavascriptAppModalDialog jsDialog = getCurrentDialog();
218         assertNotNull("No dialog showing.", jsDialog);
219
220         checkButtonPresenceVisibilityText(
221                 jsDialog, 0, org.chromium.chrome.R.string.dont_reload_this_page,
222                 "Don't reload this page");
223         checkButtonPresenceVisibilityText(
224                 jsDialog, 2, org.chromium.chrome.R.string.reload_this_page,
225                 "Reload this page");
226     }
227
228     /**
229      * Verifies that repeated dialogs give the option to disable dialogs
230      * altogether and then that disabling them works.
231      */
232     @MediumTest
233     @Feature({"Browser", "Main"})
234     public void testDisableRepeatedDialogs()
235             throws InterruptedException, TimeoutException, ExecutionException {
236         OnEvaluateJavaScriptResultHelper scriptEvent =
237                 executeJavaScriptAndWaitForDialog("alert('Android');");
238
239         // Show a dialog once.
240         JavascriptAppModalDialog jsDialog = getCurrentDialog();
241         assertNotNull("No dialog showing.", jsDialog);
242
243         clickCancel(jsDialog);
244         scriptEvent.waitUntilHasValue();
245
246         // Show it again, it should have the option to suppress subsequent dialogs.
247         scriptEvent = executeJavaScriptAndWaitForDialog("alert('Android');");
248         jsDialog = getCurrentDialog();
249         assertNotNull("No dialog showing.", jsDialog);
250         final AlertDialog dialog = jsDialog.getDialogForTest();
251         String errorMessage = ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
252             @Override
253             public String call() {
254                 final CheckBox suppress = (CheckBox) dialog.findViewById(
255                         org.chromium.chrome.R.id.suppress_js_modal_dialogs);
256                 if (suppress == null) return "Suppress checkbox not found.";
257                 if (suppress.getVisibility() != View.VISIBLE) {
258                     return "Suppress checkbox is not visible.";
259                 }
260                 suppress.setChecked(true);
261                 return null;
262             }
263         });
264         assertNull(errorMessage, errorMessage);
265         clickCancel(jsDialog);
266         scriptEvent.waitUntilHasValue();
267
268         scriptEvent.evaluateJavaScript(getActivity().getActiveContentViewCore(),
269                 "alert('Android');");
270         assertTrue("No further dialog boxes should be shown.", scriptEvent.waitUntilHasValue());
271     }
272
273     /**
274      * Displays a dialog and closes the tab in the background before attempting
275      * to accept the dialog. Verifies that the dialog is dismissed when the tab
276      * is closed.
277      */
278     @MediumTest
279     @Feature({"Browser", "Main"})
280     public void testDialogDismissedAfterClosingTab() throws InterruptedException {
281         executeJavaScriptAndWaitForDialog("alert('Android')");
282
283         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
284             @Override
285             public void run() {
286                 getActivity().createTab(EMPTY_PAGE);
287             }
288         });
289
290         // Closing the tab should have dismissed the dialog.
291         boolean criteriaSatisfied = CriteriaHelper.pollForCriteria(
292                 new JavascriptAppModalDialogShownCriteria(false));
293         assertTrue("The dialog should have been dismissed when its tab was closed.",
294                 criteriaSatisfied);
295     }
296
297     /**
298      * Asynchronously executes the given code for spawning a dialog and waits
299      * for the dialog to be visible.
300      */
301     private OnEvaluateJavaScriptResultHelper executeJavaScriptAndWaitForDialog(String script)
302             throws InterruptedException {
303         return executeJavaScriptAndWaitForDialog(new OnEvaluateJavaScriptResultHelper(), script);
304     }
305
306     /**
307      * Given a JavaScript evaluation helper, asynchronously executes the given
308      * code for spawning a dialog and waits for the dialog to be visible.
309      */
310     private OnEvaluateJavaScriptResultHelper executeJavaScriptAndWaitForDialog(
311             final OnEvaluateJavaScriptResultHelper helper, String script)
312             throws InterruptedException {
313         helper.evaluateJavaScript(getActivity().getActiveContentViewCore(),
314                 script);
315         boolean criteriaSatisfied = CriteriaHelper.pollForCriteria(
316                 new JavascriptAppModalDialogShownCriteria(true));
317         assertTrue("Could not spawn or locate a modal dialog.", criteriaSatisfied);
318         return helper;
319     }
320
321     /**
322      * Returns an array of the 3 buttons for this dialog, in the order
323      * BUTTON_NEGATIVE, BUTTON_NEUTRAL and BUTTON_POSITIVE. Any of these values
324      * can be null.
325      */
326     private Button[] getAlertDialogButtons(final AlertDialog dialog) throws ExecutionException {
327         return ThreadUtils.runOnUiThreadBlocking(new Callable<Button[]>() {
328             @Override
329             public Button[] call() {
330                 final Button[] buttons = new Button[3];
331                 buttons[0] = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
332                 buttons[1] = dialog.getButton(DialogInterface.BUTTON_NEUTRAL);
333                 buttons[2] = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
334                 return buttons;
335             }
336         });
337     }
338
339     /**
340      * Returns the current JavaScript modal dialog showing or null if no such dialog is currently
341      * showing.
342      */
343     private JavascriptAppModalDialog getCurrentDialog() throws ExecutionException {
344         return ThreadUtils.runOnUiThreadBlocking(new Callable<JavascriptAppModalDialog>() {
345             @Override
346             public JavascriptAppModalDialog call() {
347                 return JavascriptAppModalDialog.getCurrentDialogForTest();
348             }
349         });
350     }
351
352     private static class JavascriptAppModalDialogShownCriteria implements Criteria {
353         private final boolean mShouldBeShown;
354
355         public JavascriptAppModalDialogShownCriteria(boolean shouldBeShown) {
356             mShouldBeShown = shouldBeShown;
357         }
358
359         @Override
360         public boolean isSatisfied() {
361             try {
362                 return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
363                     @Override
364                     public Boolean call() {
365                         final boolean isShown =
366                                 JavascriptAppModalDialog.getCurrentDialogForTest() != null;
367                         return mShouldBeShown == isShown;
368                     }
369                 });
370             } catch (ExecutionException e) {
371                 Log.e(TAG, "Failed to getCurrentDialog", e);
372                 return false;
373             }
374         }
375     }
376
377     /**
378      * Simulates pressing the OK button of the passed dialog.
379      */
380     private void clickOk(JavascriptAppModalDialog dialog) {
381         clickButton(dialog, DialogInterface.BUTTON_POSITIVE);
382     }
383
384     /**
385      * Simulates pressing the Cancel button of the passed dialog.
386      */
387     private void clickCancel(JavascriptAppModalDialog dialog) {
388         clickButton(dialog, DialogInterface.BUTTON_NEGATIVE);
389     }
390
391     private void clickButton(final JavascriptAppModalDialog dialog, final int whichButton) {
392         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
393             @Override
394             public void run() {
395                 dialog.onClick(null, whichButton);
396             }
397         });
398     }
399
400     private void checkButtonPresenceVisibilityText(
401             JavascriptAppModalDialog jsDialog, int buttonIndex,
402             int expectedTextResourceId, String readableName) throws ExecutionException {
403         final Button[] buttons = getAlertDialogButtons(jsDialog.getDialogForTest());
404         final Button button = buttons[buttonIndex];
405         assertNotNull("No '" + readableName + "' button in confirm dialog.", button);
406         assertEquals("'" + readableName + "' button is not visible.",
407                 View.VISIBLE,
408                 button.getVisibility());
409         assertEquals("'" + readableName + "' button has wrong text",
410                 getActivity().getResources().getString(expectedTextResourceId),
411                 button.getText().toString());
412     }
413
414     private TestCallbackHelperContainer getActiveTabTestCallbackHelperContainer() {
415         return new TestCallbackHelperContainer(getActivity().getActiveTab().getContentViewCore());
416     }
417 }