Bug 1247366 - AppMenuComponent.findAppMenuItemView(): Wait for views and not for text to appear. r?margaret
MozReview-Commit-ID: Hb5BHnu15nm
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
@@ -5,16 +5,17 @@
package org.mozilla.gecko.tests.components;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertEquals;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertFalse;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
import java.util.List;
+import java.util.concurrent.Callable;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.menu.MenuItemActionBar;
import org.mozilla.gecko.menu.MenuItemDefault;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.helpers.DeviceHelper;
import org.mozilla.gecko.tests.helpers.RobotiumHelper;
@@ -28,17 +29,17 @@ import android.widget.RelativeLayout;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.RobotiumUtils;
import com.jayway.android.robotium.solo.Solo;
/**
* A class representing any interactions that take place on the app menu.
*/
public class AppMenuComponent extends BaseComponent {
- private static final long MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 7500L;
+ private static final int MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 7500;
public enum MenuItem {
FORWARD(R.string.forward),
NEW_TAB(R.string.new_tab),
PAGE(R.string.page),
RELOAD(R.string.reload);
private final int resourceID;
@@ -119,50 +120,53 @@ public class AppMenuComponent extends Ba
* Try to find a MenuItemActionBar/MenuItemDefault with the given text set as contentDescription / text.
*
* When using legacy menus, make sure the menu has been opened to the appropriate level
* (i.e. base menu or "More" menu) to ensure the appropriate menu views are in memory.
* TODO: ^ Maybe we just need to have opened the "More" menu and the current one doesn't matter.
*
* This method is dependent on not having two views with equivalent contentDescription / text.
*/
- private View findAppMenuItemView(String text) {
- RobotiumHelper.waitForExactText(text, 1, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
+ private View findAppMenuItemView(final String text) {
+ return WaitHelper.waitFor(String.format("menu item view '%s'", text), new Callable<View>() {
+ @Override
+ public View call() throws Exception {
+ final List<View> views = mSolo.getViews();
- final List<View> views = mSolo.getViews();
+ final List<MenuItemActionBar> menuItemActionBarList = RobotiumUtils.filterViews(MenuItemActionBar.class, views);
+ for (MenuItemActionBar menuItem : menuItemActionBarList) {
+ if (TextUtils.equals(menuItem.getContentDescription(), text)) {
+ return menuItem;
+ }
+ }
- final List<MenuItemActionBar> menuItemActionBarList = RobotiumUtils.filterViews(MenuItemActionBar.class, views);
- for (MenuItemActionBar menuItem : menuItemActionBarList) {
- if (TextUtils.equals(menuItem.getContentDescription(), text)) {
- return menuItem;
- }
- }
+ final List<MenuItemDefault> menuItemDefaultList = RobotiumUtils.filterViews(MenuItemDefault.class, views);
+ for (MenuItemDefault menuItem : menuItemDefaultList) {
+ if (TextUtils.equals(menuItem.getText(), text)) {
+ return menuItem;
+ }
+ }
- final List<MenuItemDefault> menuItemDefaultList = RobotiumUtils.filterViews(MenuItemDefault.class, views);
- for (MenuItemDefault menuItem : menuItemDefaultList) {
- if (TextUtils.equals(menuItem.getText(), text)) {
- return menuItem;
+ // On Android 2.3, menu items may be instances of
+ // com.android.internal.view.menu.ListMenuItemView, each with a child
+ // android.widget.RelativeLayout which in turn has a child
+ // TextView with the appropriate text.
+ final List<TextView> textViewList = RobotiumUtils.filterViews(TextView.class, views);
+ for (TextView textView : textViewList) {
+ if (TextUtils.equals(textView.getText(), text)) {
+ View relativeLayout = (View) textView.getParent();
+ if (relativeLayout instanceof RelativeLayout) {
+ View listMenuItemView = (View)relativeLayout.getParent();
+ return listMenuItemView;
+ }
+ }
+ }
+ return null;
}
- }
-
- // On Android 2.3, menu items may be instances of
- // com.android.internal.view.menu.ListMenuItemView, each with a child
- // android.widget.RelativeLayout which in turn has a child
- // TextView with the appropriate text.
- final List<TextView> textViewList = RobotiumUtils.filterViews(TextView.class, views);
- for (TextView textView : textViewList) {
- if (TextUtils.equals(textView.getText(), text)) {
- View relativeLayout = (View) textView.getParent();
- if (relativeLayout instanceof RelativeLayout) {
- View listMenuItemView = (View)relativeLayout.getParent();
- return listMenuItemView;
- }
- }
- }
- return null;
+ }, MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS);
}
/**
* Helper function to let Robotium locate and click menu item from legacy Android menu (devices with Android 2.x).
*
* Robotium will also try to open the menu if there are no open dialog.
*
* @param menuItemTitle, The title of menu item to open.
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/WaitHelper.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/helpers/WaitHelper.java
@@ -3,16 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.tests.helpers;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
import android.os.SystemClock;
+
+import java.util.concurrent.Callable;
import java.util.regex.Pattern;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.Actions.EventExpecter;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.UITestContext.ComponentType;
import org.mozilla.gecko.tests.components.ToolbarComponent;
@@ -66,16 +68,46 @@ public final class WaitHelper {
* AssertionError if the duration is elapsed and the condition is not satisfied.
*/
public static void waitFor(String message, final Condition condition, final int waitMillis) {
message = "Waiting for " + message + " with timeout " + waitMillis + ".";
fAssertTrue(message, sSolo.waitForCondition(condition, waitMillis));
}
/**
+ * Waits for the given Callable to return something that is not null, using the given wait
+ * duration; will throw an AssertionError if the duration is elapsed and the callable has not
+ * returned a non-null object.
+ *
+ * @return the value returned by the Callable. Or null if the duration has elapsed.
+ */
+ public static <V> V waitFor(String message, final Callable<V> callable, int waitMillis) {
+ sContext.dumpLog("WaitHelper", "Waiting for " + message + " with timeout " + waitMillis + ".");
+
+ final Object[] value = new Object[1];
+
+ Condition condition = new Condition() {
+ @Override
+ public boolean isSatisfied() {
+ try {
+ V result = callable.call();
+ value[0] = result;
+ return result != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ };
+
+ sSolo.waitForCondition(condition, waitMillis);
+
+ return (V) value[0];
+ }
+
+ /**
* Waits for the Gecko event declaring the page has loaded. Takes in and runs a Runnable
* that will perform the action that will cause the page to load.
*/
public static void waitForPageLoad(final Runnable initiatingAction) {
fAssertNotNull("initiatingAction is not null", initiatingAction);
// Some changes to the UI occur in response to the same event we listen to for when
// the page has finished loading (e.g. a page title update). As such, we ensure this