Index: chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestCaseBase.java |
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestCaseBase.java b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestCaseBase.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a5f222b0a509f4756cf1817a977028d1cfcf6158 |
--- /dev/null |
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/permissions/PermissionTestCaseBase.java |
@@ -0,0 +1,261 @@ |
+// Copyright 2016 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.permissions; |
+ |
+import android.content.DialogInterface; |
+import android.support.v7.app.AlertDialog; |
+import android.support.v7.widget.SwitchCompat; |
+ |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.base.test.util.CallbackHelper; |
+import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.ChromeActivity; |
+import org.chromium.chrome.browser.infobar.InfoBar; |
+import org.chromium.chrome.browser.infobar.InfoBarContainer; |
+import org.chromium.chrome.browser.tab.EmptyTabObserver; |
+import org.chromium.chrome.browser.tab.Tab; |
+import org.chromium.chrome.test.ChromeActivityTestCaseBase; |
+import org.chromium.chrome.test.util.InfoBarTestAnimationListener; |
+import org.chromium.chrome.test.util.InfoBarUtil; |
+import org.chromium.content.browser.test.util.Criteria; |
+import org.chromium.content.browser.test.util.CriteriaHelper; |
+import org.chromium.net.test.EmbeddedTestServer; |
+ |
+import java.util.concurrent.Callable; |
+import java.util.concurrent.ExecutionException; |
+ |
+/** |
+ * Test case base for permissions UI testing on Android. |
+ * |
+ * This class allows for easy testing of permissions infobar and dialog prompts. Writing a test |
+ * simply requires a HTML file containing JavaScript methods which trigger a permission prompt. The |
+ * methods should update the page's title with <prefix>: <count>, where <count> is the number of |
+ * updates expected (usually 1, although some APIs like Geolocation's watchPosition may trigger |
+ * callbacks repeatedly). |
+ * |
+ * Subclasses may then call runAllowTest to start a test server, navigate to the provided HTML page, |
+ * and run the JavaScript method. The permission will be granted, and the test will verify that the |
+ * page title is updated as expected. |
+ * |
+ * runAllowTest has several parameters to specify the conditions of the test, including whether |
+ * a persistence toggle is expected, whether it should be explicitly toggled, whether to trigger the |
+ * JS call with a gesture, and whether an infobar or a dialog is expected. |
+ */ |
+public class PermissionTestCaseBase extends ChromeActivityTestCaseBase<ChromeActivity> { |
+ protected static final String MODAL_FLAG = "ModalPermissionPrompts"; |
+ protected static final String TOGGLE_FLAG = "DisplayPersistenceToggleInPermissionPrompts"; |
+ protected static final String MODAL_TOGGLE_FLAG = MODAL_FLAG + "," + TOGGLE_FLAG; |
+ protected static final String NO_GESTURE_FEATURE = |
+ "enable-features=ModalPermissionPrompts<ModalPrompts"; |
+ protected static final String FORCE_FIELDTRIAL = "force-fieldtrials=ModalPrompts/Group1"; |
+ protected static final String FORCE_FIELDTRIAL_PARAMS = |
+ "force-fieldtrial-params=ModalPrompts.Group1:require_gesture/false"; |
+ |
+ private InfoBarTestAnimationListener mListener; |
+ private EmbeddedTestServer mTestServer; |
+ |
+ /** |
+ * Waits till a JavaScript callback which updates the page title is called the specified number |
+ * of times. The page title is expected to be of the form <prefix>: <count>. |
+ */ |
+ protected class PermissionUpdateWaiter extends EmptyTabObserver { |
+ private CallbackHelper mCallbackHelper; |
+ private String mPrefix; |
+ private int mExpectedCount; |
+ |
+ public PermissionUpdateWaiter(String prefix) { |
+ mCallbackHelper = new CallbackHelper(); |
+ mPrefix = prefix; |
+ } |
+ |
+ @Override |
+ public void onTitleUpdated(Tab tab) { |
+ String expectedTitle = mPrefix + mExpectedCount; |
+ if (getActivity().getActivityTab().getTitle().equals(expectedTitle)) { |
+ mCallbackHelper.notifyCalled(); |
+ } |
+ } |
+ |
+ public void waitForNumUpdates(int numUpdates) throws Exception { |
+ mExpectedCount = numUpdates; |
+ mCallbackHelper.waitForCallback(0); |
+ } |
+ } |
+ |
+ /** |
+ * Criteria class to detect whether the permission dialog is shown. |
+ */ |
+ private static class DialogShownCriteria extends Criteria { |
+ private AlertDialog mDialog; |
+ |
+ public DialogShownCriteria(String error) { |
+ super(error); |
+ } |
+ |
+ public AlertDialog getDialog() { |
+ return mDialog; |
+ } |
+ |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ return ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() { |
+ mDialog = PermissionDialogController.getInstance() |
+ .getCurrentDialogForTesting(); |
+ return mDialog != null; |
+ } |
+ }); |
+ } catch (ExecutionException e) { |
+ return false; |
+ } |
+ } |
+ } |
+ |
+ public PermissionTestCaseBase() { |
+ super(ChromeActivity.class); |
+ } |
+ |
+ @Override |
+ protected void setUp() throws Exception { |
+ super.setUp(); |
+ InfoBarContainer container = |
+ getActivity().getTabModelSelector().getCurrentTab().getInfoBarContainer(); |
+ mListener = new InfoBarTestAnimationListener(); |
+ container.setAnimationListener(mListener); |
+ mTestServer = EmbeddedTestServer.createAndStartServer(getInstrumentation().getContext()); |
+ } |
+ |
+ @Override |
+ protected void tearDown() throws Exception { |
+ mTestServer.stopAndDestroyServer(); |
+ super.tearDown(); |
+ } |
+ |
+ /** |
+ * Simulates clicking a button on an AlertDialog. |
+ */ |
+ private void clickButton(final AlertDialog dialog, final int button) { |
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
+ @Override |
+ public void run() { |
+ dialog.getButton(button).performClick(); |
+ } |
+ }); |
+ } |
+ |
+ /** |
+ * Runs a permission prompt test that grants the permission and expects the page title to be |
+ * updated in response. |
+ * @param updateWaiter The update waiter to wait for callbacks. Should be added as an observer |
+ * to the current tab prior to calling this method. |
+ * @param javascript The JS function to run in the current tab to execute the test and update |
+ * the page title. |
+ * @param nUpdates How many updates of the page title to wait for. |
+ * @param withGeature True if we require a user gesture to trigger the prompt. |
+ * @param isDialog True if we are expecting a permission dialog, false for an infobar. |
+ * @param hasSwitch True if we are expecting a persistence switch, false otherwise. |
+ * @param toggleSwitch True if we should toggle the switch off, false otherwise. |
+ * @throws Exception |
+ */ |
+ protected void runAllowTest(PermissionUpdateWaiter updateWaiter, final String url, |
+ String javascript, int nUpdates, boolean withGesture, boolean isDialog, |
+ boolean hasSwitch, boolean toggleSwitch) throws Exception { |
+ final String test_url = mTestServer.getURL(url); |
+ loadUrl(test_url); |
+ |
+ if (withGesture) { |
+ runJavaScriptCodeInCurrentTab("functionToRun = '" + javascript + "'"); |
+ singleClickView(getActivity().getActivityTab().getView()); |
+ } else { |
+ runJavaScriptCodeInCurrentTab(javascript); |
+ } |
+ |
+ if (isDialog) { |
+ DialogShownCriteria criteria = new DialogShownCriteria("Dialog not shown"); |
+ CriteriaHelper.pollInstrumentationThread(criteria); |
+ replyToDialogAndWaitForUpdates( |
+ updateWaiter, criteria.getDialog(), nUpdates, true, hasSwitch, toggleSwitch); |
+ } else { |
+ replyToInfoBarAndWaitForUpdates(updateWaiter, nUpdates, true, hasSwitch, toggleSwitch); |
+ } |
+ } |
+ |
+ /** |
+ * Replies to an infobar permission prompt, optionally checking for the presence of a |
+ * persistence switch and toggling it. Waits for a provided number of updates to the page title |
+ * in response. |
+ */ |
+ private void replyToInfoBarAndWaitForUpdates(PermissionUpdateWaiter updateWaiter, int nUpdates, |
+ boolean allow, boolean hasSwitch, boolean toggleSwitch) throws Exception { |
+ assertTrue("InfoBar not added.", mListener.addInfoBarAnimationFinished()); |
+ InfoBar infobar = getInfoBars().get(0); |
+ assertNotNull(infobar); |
+ |
+ if (hasSwitch) { |
+ SwitchCompat persistSwitch = (SwitchCompat) infobar.getView().findViewById( |
+ R.id.permission_infobar_persist_toggle); |
+ assertNotNull(persistSwitch); |
+ assertTrue(persistSwitch.isChecked()); |
+ if (toggleSwitch) { |
+ singleClickView(persistSwitch); |
+ waitForCheckedState(persistSwitch, false); |
+ } |
+ } |
+ |
+ if (allow) { |
+ assertTrue("Allow button wasn't found", InfoBarUtil.clickPrimaryButton(infobar)); |
+ } else { |
+ assertTrue("Block button wasn't found", InfoBarUtil.clickSecondaryButton(infobar)); |
+ } |
+ updateWaiter.waitForNumUpdates(nUpdates); |
+ } |
+ |
+ /** |
+ * Replies to a dialog permission prompt, optionally checking for the presence of a |
+ * persistence switch and toggling it. Waits for a provided number of updates to the page title |
+ * in response. |
+ */ |
+ private void replyToDialogAndWaitForUpdates(PermissionUpdateWaiter updateWaiter, |
+ AlertDialog dialog, int nUpdates, boolean allow, boolean hasSwitch, |
+ boolean toggleSwitch) throws Exception { |
+ if (hasSwitch) { |
+ SwitchCompat persistSwitch = |
+ (SwitchCompat) dialog.findViewById(R.id.permission_dialog_persist_toggle); |
+ assertNotNull(persistSwitch); |
+ assertTrue(persistSwitch.isChecked()); |
+ if (toggleSwitch) { |
+ singleClickView(persistSwitch); |
+ waitForCheckedState(persistSwitch, false); |
+ } |
+ } |
+ |
+ if (allow) { |
+ clickButton(dialog, DialogInterface.BUTTON_POSITIVE); |
+ } else { |
+ clickButton(dialog, DialogInterface.BUTTON_NEGATIVE); |
+ } |
+ updateWaiter.waitForNumUpdates(nUpdates); |
+ } |
+ |
+ /** |
+ * Waits until the provided switch reaches a specified position (checked or unchecked). |
+ */ |
+ private void waitForCheckedState(final SwitchCompat persistSwitch, boolean isChecked) |
+ throws InterruptedException { |
+ CriteriaHelper.pollUiThread(Criteria.equals(isChecked, new Callable<Boolean>() { |
+ @Override |
+ public Boolean call() { |
+ return persistSwitch.isChecked(); |
+ } |
+ })); |
+ } |
+ |
+ @Override |
+ public void startMainActivity() throws InterruptedException { |
+ startMainActivityOnBlankPage(); |
+ } |
+} |