[Android] Create ViewElement#createSettleCondition()
After closing the soft keyboard, wait for views of the NewTabGroupDialogFacility to settle. Re-enable TabGroupDialogPTTest which is likely flaky due to the dialog moving after the soft keyboard closes, while we are generating an input event for it. Bug: 391888241 Change-Id: If2fe0dd6b791bba9ee3042899a2bcfb2898d9309 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6190496 Reviewed-by: Sky Malice <skym@chromium.org> Commit-Queue: Henrique Nakashima <hnakashima@chromium.org> Cr-Commit-Position: refs/heads/main@{#1411622}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b924ba930e
commit
345b51b68b
base/test/android/javatests/src/org/chromium/base/test/transit
chrome
android
features
tab_ui
javatests
src
org
chromium
chrome
browser
tasks
tab_management
test
android
javatests
src
org
chromium
chrome
test
@ -45,6 +45,11 @@ public class ViewConditions {
|
||||
private final Matcher<View> mMatcher;
|
||||
private final Options mOptions;
|
||||
private View mViewMatched;
|
||||
private int mPreviousViewX = Integer.MIN_VALUE;
|
||||
private int mPreviousViewY = Integer.MIN_VALUE;
|
||||
private int mPreviousViewWidth = Integer.MIN_VALUE;
|
||||
private int mPreviousViewHeight = Integer.MIN_VALUE;
|
||||
private long mLastChangeMs = -1;
|
||||
|
||||
public DisplayedCondition(Matcher<View> matcher, Options options) {
|
||||
super(/* isRunOnUiThread= */ false);
|
||||
@ -61,6 +66,9 @@ public class ViewConditions {
|
||||
.append(" (>= ")
|
||||
.append(mOptions.mDisplayedPercentageRequired)
|
||||
.append("% displayed");
|
||||
if (mOptions.mSettleTimeMs > 0) {
|
||||
description.append(", settled for ").append(mOptions.mSettleTimeMs).append("ms");
|
||||
}
|
||||
if (mOptions.mExpectEnabled) {
|
||||
description.append(", enabled");
|
||||
}
|
||||
@ -143,6 +151,7 @@ public class ViewConditions {
|
||||
messages.add(String.format("%d%% displayed", portion.mPercentage));
|
||||
}
|
||||
}
|
||||
|
||||
if (mOptions.mExpectEnabled) {
|
||||
if (!mViewMatched.isEnabled()) {
|
||||
fulfilled = false;
|
||||
@ -155,6 +164,34 @@ public class ViewConditions {
|
||||
}
|
||||
}
|
||||
|
||||
if (mOptions.mSettleTimeMs > 0) {
|
||||
long nowMs = System.currentTimeMillis();
|
||||
int[] locationOnScreen = new int[2];
|
||||
mViewMatched.getLocationOnScreen(locationOnScreen);
|
||||
int newX = locationOnScreen[0];
|
||||
int newY = locationOnScreen[1];
|
||||
int newWidth = view.getWidth();
|
||||
int newHeight = view.getHeight();
|
||||
if (mPreviousViewX != newX
|
||||
|| mPreviousViewY != newY
|
||||
|| mPreviousViewWidth != newWidth
|
||||
|| mPreviousViewHeight != newHeight) {
|
||||
mPreviousViewX = newX;
|
||||
mPreviousViewY = newY;
|
||||
mPreviousViewWidth = newWidth;
|
||||
mPreviousViewHeight = newHeight;
|
||||
mLastChangeMs = nowMs;
|
||||
}
|
||||
|
||||
long timeSinceMoveMs = nowMs - mLastChangeMs;
|
||||
if (timeSinceMoveMs < mOptions.mSettleTimeMs) {
|
||||
fulfilled = false;
|
||||
messages.add("Not settled for " + mOptions.mSettleTimeMs + "ms");
|
||||
} else {
|
||||
messages.add("Settled for " + mOptions.mSettleTimeMs + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
String message = String.join("; ", messages);
|
||||
if (fulfilled) {
|
||||
return fulfilled(message).withResult(mViewMatched);
|
||||
@ -184,6 +221,7 @@ public class ViewConditions {
|
||||
boolean mExpectEnabled = true;
|
||||
boolean mExpectDisabled;
|
||||
int mDisplayedPercentageRequired = ViewElement.MIN_DISPLAYED_PERCENT;
|
||||
int mSettleTimeMs;
|
||||
|
||||
private Options() {}
|
||||
|
||||
@ -209,6 +247,12 @@ public class ViewConditions {
|
||||
mDisplayedPercentageRequired = displayedPercentageRequired;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** How long the View's rect needs to be unchanged. */
|
||||
public Builder withSettleTimeMs(int settleTimeMs) {
|
||||
mSettleTimeMs = settleTimeMs;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,23 @@ public class ViewElement extends Element<View> {
|
||||
.withExpectEnabled(mOptions.mExpectEnabled)
|
||||
.withExpectDisabled(mOptions.mExpectDisabled)
|
||||
.withDisplayingAtLeast(mOptions.mDisplayedPercentageRequired)
|
||||
.withSettleTimeMs(mOptions.mInitialSettleTimeMs)
|
||||
.build();
|
||||
return new DisplayedCondition(viewMatcher, conditionOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link DisplayedCondition} like the enter Condition, but also waiting for the View
|
||||
* to settle (no changes to its rect coordinates) for 1 second.
|
||||
*/
|
||||
public ConditionWithResult<View> createSettleCondition() {
|
||||
Matcher<View> viewMatcher = mViewSpec.getViewMatcher();
|
||||
DisplayedCondition.Options conditionOptions =
|
||||
DisplayedCondition.newOptions()
|
||||
.withExpectEnabled(mOptions.mExpectEnabled)
|
||||
.withExpectDisabled(mOptions.mExpectDisabled)
|
||||
.withDisplayingAtLeast(mOptions.mDisplayedPercentageRequired)
|
||||
.withSettleTimeMs(1000)
|
||||
.build();
|
||||
return new DisplayedCondition(viewMatcher, conditionOptions);
|
||||
}
|
||||
@ -80,7 +97,8 @@ public class ViewElement extends Element<View> {
|
||||
protected boolean mExpectEnabled = true;
|
||||
protected boolean mExpectDisabled;
|
||||
protected String mElementId;
|
||||
protected Integer mDisplayedPercentageRequired = ViewElement.MIN_DISPLAYED_PERCENT;
|
||||
protected int mDisplayedPercentageRequired = ViewElement.MIN_DISPLAYED_PERCENT;
|
||||
protected int mInitialSettleTimeMs;
|
||||
|
||||
protected Options() {}
|
||||
|
||||
@ -132,6 +150,12 @@ public class ViewElement extends Element<View> {
|
||||
mDisplayedPercentageRequired = percentage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Waits for the View's rect to stop moving. */
|
||||
public Builder initialSettleTime(int settleTimeMs) {
|
||||
mInitialSettleTimeMs = settleTimeMs;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.chromium.base.test.util.Batch;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.DisabledTest;
|
||||
import org.chromium.base.test.util.Features.EnableFeatures;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
@ -48,7 +47,6 @@ public class TabGroupDialogPTTest {
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
@DisabledTest(message = "crbug.com/381595663")
|
||||
public void testNewTabCreation() {
|
||||
WebPageStation firstPage = mInitialStateRule.startOnBlankPage();
|
||||
WebPageStation pageStation =
|
||||
@ -67,7 +65,6 @@ public class TabGroupDialogPTTest {
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
@DisabledTest(message = "crbug.com/381595663")
|
||||
public void testIncognitoNewTabCreation() {
|
||||
WebPageStation firstPage = mInitialStateRule.startOnBlankPage();
|
||||
WebPageStation pageStation =
|
||||
@ -86,7 +83,6 @@ public class TabGroupDialogPTTest {
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
@DisabledTest(message = "crbug.com/381595663")
|
||||
public void testTabGroupNameChange() {
|
||||
WebPageStation firstPage = mInitialStateRule.startOnBlankPage();
|
||||
WebPageStation pageStation =
|
||||
|
@ -9,6 +9,8 @@ import androidx.test.espresso.Espresso;
|
||||
import org.chromium.base.test.transit.Elements;
|
||||
import org.chromium.base.test.transit.Facility;
|
||||
import org.chromium.base.test.transit.Station;
|
||||
import org.chromium.base.test.transit.Transition;
|
||||
import org.chromium.base.test.transit.ViewElement;
|
||||
import org.chromium.ui.test.transit.SoftKeyboardElement;
|
||||
|
||||
/** Represents the soft keyboard shown, expecting it to hide after exiting the Facility. */
|
||||
@ -21,7 +23,15 @@ public class SoftKeyboardFacility extends Facility<Station<?>> {
|
||||
elements.declareElement(new SoftKeyboardElement(mHostStation.getActivityElement()));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
/**
|
||||
* Close the soft keyboard and wait for the passed ViewElements to stop moving after the
|
||||
* relayout.
|
||||
*
|
||||
* <p>If it was expected to not be shown, just ensure that and exit this Facility.
|
||||
*
|
||||
* @param viewElementsToSettle the ViewElements to wait to stop moving
|
||||
*/
|
||||
public void close(ViewElement... viewElementsToSettle) {
|
||||
assertSuppliersCanBeUsed();
|
||||
|
||||
if (mSoftKeyboardElement.get()) {
|
||||
@ -29,7 +39,11 @@ public class SoftKeyboardFacility extends Facility<Station<?>> {
|
||||
|
||||
// If this fails, the keyboard was closed before, but not by this facility.
|
||||
recheckActiveConditions();
|
||||
mHostStation.exitFacilitySync(this, Espresso::pressBack);
|
||||
Transition.TransitionOptions.Builder options = Transition.newOptions();
|
||||
for (ViewElement viewElement : viewElementsToSettle) {
|
||||
options.withCondition(viewElement.createSettleCondition());
|
||||
}
|
||||
mHostStation.exitFacilitySync(this, options.build(), Espresso::pressBack);
|
||||
} else {
|
||||
// Keyboard was not expected to be shown
|
||||
mHostStation.exitFacilitySync(this, /* trigger= */ null);
|
||||
|
@ -60,6 +60,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
private final @Nullable @TabGroupColorId Integer mSelectedColor;
|
||||
private final SoftKeyboardFacility mSoftKeyboard;
|
||||
private ViewSpec mTitleInputSpec;
|
||||
private ViewElement mDialog;
|
||||
|
||||
/** Constructor. Expects no particular title or selected color. */
|
||||
public NewTabGroupDialogFacility(
|
||||
@ -85,7 +86,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
|
||||
@Override
|
||||
public void declareElements(Elements.Builder elements) {
|
||||
elements.declareView(DIALOG, ViewElement.displayingAtLeastOption(80));
|
||||
mDialog = elements.declareView(DIALOG, ViewElement.displayingAtLeastOption(80));
|
||||
elements.declareView(DIALOG_TITLE);
|
||||
|
||||
String inputElementId = "Tab group title input showing " + mTitle;
|
||||
@ -132,14 +133,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
// An empty name causes warning text to show up which could push the color picker container
|
||||
// out of view for small screen devices, so dismiss the keyboard.
|
||||
if (newTabGroupName.isEmpty()) {
|
||||
if (mSoftKeyboard.getPhase() == Phase.ACTIVE) {
|
||||
mSoftKeyboard.close();
|
||||
} else if (mSoftKeyboard.getPhase() == Phase.FINISHED) {
|
||||
// Do nothing as the soft keyboard has already been closed
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"SoftKeyboardFacility is in phase " + mSoftKeyboard.getPhase());
|
||||
}
|
||||
ensureSoftKeyboardClosed();
|
||||
}
|
||||
|
||||
return mHostStation.swapFacilitySync(
|
||||
@ -159,14 +153,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
|
||||
/** Press "Done" to confirm the tab group name and color. */
|
||||
public TabSwitcherGroupCardFacility pressDone() {
|
||||
if (mSoftKeyboard.getPhase() == Phase.ACTIVE) {
|
||||
mSoftKeyboard.close();
|
||||
} else if (mSoftKeyboard.getPhase() == Phase.FINISHED) {
|
||||
// Do nothing as the soft keyboard has already been closed
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"SoftKeyboardFacility is in phase " + mSoftKeyboard.getPhase());
|
||||
}
|
||||
ensureSoftKeyboardClosed();
|
||||
|
||||
// The reason we can pass an expected card index is because the tab group has already been
|
||||
// created.
|
||||
@ -180,14 +167,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
|
||||
/** Press "Done" to confirm the tab group name and color, but no-op from an invalid title. */
|
||||
public NewTabGroupDialogFacility pressDoneWithInvalidTitle() {
|
||||
if (mSoftKeyboard.getPhase() == Phase.ACTIVE) {
|
||||
mSoftKeyboard.close();
|
||||
} else if (mSoftKeyboard.getPhase() == Phase.FINISHED) {
|
||||
// Do nothing as the soft keyboard has already been closed
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"SoftKeyboardFacility is in phase " + mSoftKeyboard.getPhase());
|
||||
}
|
||||
ensureSoftKeyboardClosed();
|
||||
|
||||
return mHostStation.swapFacilitySync(
|
||||
this,
|
||||
@ -199,14 +179,7 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
|
||||
/** Press the system backpress to confirm the tab group name and color. */
|
||||
public TabSwitcherGroupCardFacility pressBack() {
|
||||
if (mSoftKeyboard.getPhase() == Phase.ACTIVE) {
|
||||
mSoftKeyboard.close();
|
||||
} else if (mSoftKeyboard.getPhase() == Phase.FINISHED) {
|
||||
// Do nothing as the soft keyboard has already been closed
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"SoftKeyboardFacility is in phase " + mSoftKeyboard.getPhase());
|
||||
}
|
||||
ensureSoftKeyboardClosed();
|
||||
|
||||
// The reason we can pass an expected card index is because the tab group has already been
|
||||
// created.
|
||||
@ -219,4 +192,15 @@ public class NewTabGroupDialogFacility extends Facility<TabSwitcherStation> {
|
||||
Espresso.pressBack();
|
||||
});
|
||||
}
|
||||
|
||||
private void ensureSoftKeyboardClosed() {
|
||||
if (mSoftKeyboard.getPhase() == Phase.ACTIVE) {
|
||||
mSoftKeyboard.close(mDialog);
|
||||
} else if (mSoftKeyboard.getPhase() == Phase.FINISHED) {
|
||||
// Do nothing as the soft keyboard has already been closed
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"SoftKeyboardFacility is in phase " + mSoftKeyboard.getPhase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user