diff options
Diffstat (limited to 'base')
4 files changed, 123 insertions, 37 deletions
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java index 72249ca960..3286462525 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java +++ b/base/test/android/javatests/src/org/chromium/base/test/transit/Elements.java @@ -18,10 +18,6 @@ import java.util.List; * </pre> */ public class Elements { - - /** If passed as |id|, the description is considered the id. */ - public static final String DESCRIPTION_AS_ID = "__DESCRIPTION_AS_ID"; - static final Elements EMPTY = new Elements(); private ArrayList<ElementInState> mElementsInState = new ArrayList<>(); diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewConditions.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewConditions.java index ac9f53c1c6..2cf5a801d3 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewConditions.java +++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewConditions.java @@ -32,8 +32,8 @@ import java.util.regex.Pattern; public class ViewConditions { /** Fulfilled when a single matching View exists and is displayed. */ public static class DisplayedCondition extends ExistsCondition { - public DisplayedCondition(Matcher<View> matcher) { - super(allOf(matcher, isDisplayed())); + public DisplayedCondition(Matcher<View> matcher, ExistsCondition.Options options) { + super(allOf(matcher, isDisplayed()), options); } } @@ -46,9 +46,10 @@ public class ViewConditions { private final DisplayedCondition mDisplayedCondition; private final Condition mGate; - public GatedDisplayedCondition(Matcher<View> matcher, Condition gate) { + public GatedDisplayedCondition( + Matcher<View> matcher, Condition gate, ExistsCondition.Options options) { super(); - mDisplayedCondition = new DisplayedCondition(matcher); + mDisplayedCondition = new DisplayedCondition(matcher, options); mGate = gate; } @@ -75,11 +76,13 @@ public class ViewConditions { /** Fulfilled when a single matching View exists. */ public static class ExistsCondition extends InstrumentationThreadCondition { private final Matcher<View> mMatcher; + private final Options mOptions; private View mViewMatched; - public ExistsCondition(Matcher<View> matcher) { + public ExistsCondition(Matcher<View> matcher, Options options) { super(); - this.mMatcher = matcher; + mMatcher = matcher; + mOptions = options; } @Override @@ -115,6 +118,15 @@ public class ViewConditions { mViewMatched = view; } }); + if (mOptions.mExpectEnabled) { + if (!mViewMatched.isEnabled()) { + return notFulfilled("View displayed but disabled"); + } + } else { // Expected a displayed but disabled View. + if (mViewMatched.isEnabled()) { + return notFulfilled("View displayed but enabled"); + } + } return fulfilled(message[0]); } catch (NoMatchingViewException | NoMatchingRootException @@ -129,6 +141,32 @@ public class ViewConditions { return notFulfilled(e.getClass().getSimpleName()); } } + + /** + * @return an Options builder to customize the ViewCondition. + */ + public static Options.Builder newOptions() { + return new Options().new Builder(); + } + + /** Extra options for declaring ExistsCondition. */ + public static class Options { + boolean mExpectEnabled = true; + + private Options() {} + + public class Builder { + public Options build() { + return Options.this; + } + + /** Whether the view is expected to be enabled or disabled. */ + public Builder withExpectEnabled(boolean state) { + mExpectEnabled = state; + return this; + } + } + } } /** Fulfilled when no matching Views exist and are displayed. */ diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java index 6a44672063..444376877c 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java +++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java @@ -11,7 +11,6 @@ import static org.hamcrest.Matchers.allOf; import android.view.View; import androidx.annotation.IntDef; -import androidx.annotation.Nullable; import androidx.test.espresso.Espresso; import androidx.test.espresso.ViewAction; import androidx.test.espresso.ViewInteraction; @@ -35,6 +34,7 @@ public class ViewElement { private final Matcher<View> mViewMatcher; private final @Scope int mScope; private final String mId; + private final Options mOptions; /** Alias for {@link #sharedViewElement(Matcher)} as the default way to declare ViewElements. */ public static ViewElement viewElement(Matcher<View> viewMatcher) { @@ -42,11 +42,12 @@ public class ViewElement { } /** - * Version of {@link #sharedViewElement(Matcher, String)} using the Matcher |description| as - * |id|. + * Version of {@link #sharedViewElement(Matcher, Options)} using default Options. + * + * <p>This is a good default method to the declare ViewElements; when in doubt, use this. */ public static ViewElement sharedViewElement(Matcher<View> viewMatcher) { - return new ViewElement(viewMatcher, Scope.SHARED, /* id= */ null); + return new ViewElement(viewMatcher, Scope.SHARED, Options.DEFAULT); } /** @@ -57,19 +58,14 @@ public class ViewElement { * <p>Shared ViewElements add an EXIT condition that no View is matched unless transitioning to * a ConditionalState that declares a ViewElement with the same id (which usually means an equal * Matcher<View>). - * - * <p>This is a good default method to the declare ViewElements; when in doubt, use this. */ - public static ViewElement sharedViewElement(Matcher<View> viewMatcher, String id) { - return new ViewElement(viewMatcher, Scope.SHARED, id); + public static ViewElement sharedViewElement(Matcher<View> viewMatcher, Options options) { + return new ViewElement(viewMatcher, Scope.SHARED, options); } - /** - * Version of {@link #scopedViewElement(Matcher, String)} using the Matcher |description| as - * |id|. - */ + /** Version of {@link #scopedViewElement(Matcher, Options)} using default Options. */ public static ViewElement scopedViewElement(Matcher<View> viewMatcher) { - return new ViewElement(viewMatcher, Scope.CONDITIONAL_STATE_SCOPED, /* id= */ null); + return new ViewElement(viewMatcher, Scope.CONDITIONAL_STATE_SCOPED, Options.DEFAULT); } /** @@ -80,16 +76,13 @@ public class ViewElement { * <p>ConditionalState-scoped ViewElements are the most restrictive; they generate an EXIT * condition that no View is matched under any circumstances. */ - public static ViewElement scopedViewElement(Matcher<View> viewMatcher, String id) { - return new ViewElement(viewMatcher, Scope.CONDITIONAL_STATE_SCOPED, id); + public static ViewElement scopedViewElement(Matcher<View> viewMatcher, Options options) { + return new ViewElement(viewMatcher, Scope.CONDITIONAL_STATE_SCOPED, options); } - /** - * Version of {@link #unscopedViewElement(Matcher, String)} using the Matcher |description| as - * |id|. - */ + /** Version of {@link #unscopedViewElement(Matcher, Options)} using default Options. */ public static ViewElement unscopedViewElement(Matcher<View> viewMatcher) { - return new ViewElement(viewMatcher, Scope.UNSCOPED, /* id= */ null); + return new ViewElement(viewMatcher, Scope.UNSCOPED, Options.DEFAULT); } /** @@ -100,16 +93,17 @@ public class ViewElement { * <p>Unscoped ViewElements are the most permissive; they do not generate EXIT conditions, * therefore they may or may not be gone. */ - public static ViewElement unscopedViewElement(Matcher<View> viewMatcher, String id) { - return new ViewElement(viewMatcher, Scope.UNSCOPED, id); + public static ViewElement unscopedViewElement(Matcher<View> viewMatcher, Options options) { + return new ViewElement(viewMatcher, Scope.UNSCOPED, options); } - private ViewElement(Matcher<View> viewMatcher, @Scope int scope, @Nullable String id) { + private ViewElement(Matcher<View> viewMatcher, @Scope int scope, Options options) { mViewMatcher = viewMatcher; mScope = scope; - if (id != null) { - mId = id; + if (options.mElementId != null) { + // Use a custom id instead of the Matcher description. + mId = options.mElementId; } else { // Capture the description as soon as possible to compare ViewElements added to // different @@ -119,6 +113,8 @@ public class ViewElement { // https://crbug.com/41494895#comment7. mId = StringDescription.toString(mViewMatcher); } + + mOptions = options; } String getId() { @@ -150,4 +146,53 @@ public class ViewElement { public ViewInteraction perform(ViewAction action) { return onView().perform(action); } + + /** + * @return the Options passed when declaring the ViewElement. + */ + public Options getOptions() { + return mOptions; + } + + /** + * @return an Options builder to customize the ViewElement further. + */ + public static Options.Builder newOptions() { + return new Options().new Builder(); + } + + /** Extra options for declaring ViewElements. */ + public static class Options { + static final Options DEFAULT = new Options(); + protected boolean mExpectEnabled = true; + protected String mElementId; + + protected Options() {} + + public class Builder { + public Options build() { + return Options.this; + } + + /** Use a custom Element id instead of the Matcher<View> description. */ + public Builder elementId(String id) { + mElementId = id; + return this; + } + + /** + * Expect the View to be disabled instead of enabled. + * + * <p>This is different than passing an isEnabled() Matcher.If the matcher was, for + * example |allOf(withId(ID), isEnabled())|, the exit condition would be considered + * fulfilled if the View became disabled. Meanwhile, using this option makes the exit + * condition only be considered fulfilled if no Views |withId(ID)|, enabled or not, were + * displayed. + */ + public Builder expectDisabled() { + mExpectEnabled = false; + return this; + } + } + } } diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElementInState.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElementInState.java index d1fe0fa8f9..5aa969303a 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElementInState.java +++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElementInState.java @@ -11,6 +11,7 @@ import androidx.annotation.Nullable; import org.hamcrest.Matcher; import org.chromium.base.test.transit.ViewConditions.DisplayedCondition; +import org.chromium.base.test.transit.ViewConditions.ExistsCondition; import org.chromium.base.test.transit.ViewConditions.GatedDisplayedCondition; import org.chromium.base.test.transit.ViewConditions.NotDisplayedAnymoreCondition; import org.chromium.base.test.transit.ViewElement.Scope; @@ -39,12 +40,18 @@ class ViewElementInState implements ElementInState { mGate = gate; Matcher<View> viewMatcher = mViewElement.getViewMatcher(); + ExistsCondition.Options conditionOptions = + ExistsCondition.newOptions() + .withExpectEnabled(mViewElement.getOptions().mExpectEnabled) + .build(); if (mGate != null) { GatedDisplayedCondition gatedDisplayedCondition = - new GatedDisplayedCondition(mViewElement.getViewMatcher(), mGate); + new GatedDisplayedCondition( + mViewElement.getViewMatcher(), mGate, conditionOptions); mEnterCondition = gatedDisplayedCondition; } else { - DisplayedCondition displayedCondition = new DisplayedCondition(viewMatcher); + DisplayedCondition displayedCondition = + new DisplayedCondition(viewMatcher, conditionOptions); mEnterCondition = displayedCondition; } |