diff options
Diffstat (limited to 'libs/UiAutomatorLib/src/com/android/afwtest/uiautomator/pages/PageSkipper.java')
-rw-r--r-- | libs/UiAutomatorLib/src/com/android/afwtest/uiautomator/pages/PageSkipper.java | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/libs/UiAutomatorLib/src/com/android/afwtest/uiautomator/pages/PageSkipper.java b/libs/UiAutomatorLib/src/com/android/afwtest/uiautomator/pages/PageSkipper.java new file mode 100644 index 0000000..cc3c8c8 --- /dev/null +++ b/libs/UiAutomatorLib/src/com/android/afwtest/uiautomator/pages/PageSkipper.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.afwtest.uiautomator.pages; + +import static com.android.afwtest.common.Constants.ACTION_CHECK; +import static com.android.afwtest.common.Constants.ACTION_CLICK; +import static com.android.afwtest.common.Constants.ACTION_SCROLL; +import static com.android.afwtest.uiautomator.utils.WidgetUtils.safeClick; +import static com.android.afwtest.uiautomator.utils.WidgetUtils.safeFling; + +import android.os.SystemClock; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject2; +import android.text.TextUtils; +import android.util.Log; + +import com.android.afwtest.common.Timer; +import com.android.afwtest.common.test.OemWidget; +import com.android.afwtest.common.test.TestConfig; +import com.android.afwtest.uiautomator.utils.BySelectorHelper; +import com.android.afwtest.uiautomator.utils.WidgetUtils; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +/** + * A help class to skip pages until the expected page appears. + */ +public final class PageSkipper extends UiPage { + + private static final String TAG = "afwtest.PageSkipper"; + + /** + * Thread sleep time. + */ + private static final long THREAD_SLEEP_TIME_MS = TimeUnit.SECONDS.toMillis(3); + + /** + * Words to match when finding available "NEXT" navigation buttons. + * + * The order of the list is preferring skip over continue or next. + */ + private static final String[] NEXT_WORDS = { + "[sS]kip", "SKIP", + "[fF]inish", "FINISH", + "[dD]one", "DONE", + "[nN]ext", "NEXT", + "[cC]ontinue", "CONTINUE", + "[pP]roceed", "PROCEED", + "[yY][eE][sS]", "[oO][kK]"}; + + /** + * {@link Pattern} to match for NEXT buttons. + */ + private static final Pattern NEXT_BTN_PATTERN = + Pattern.compile(TextUtils.join("|", NEXT_WORDS)); + + /** + * Buttons with text matching {@link #NEXT_BTN_PATTERN}. + */ + private static final BySelector NAVIGATION_BTN_TEXT_SELECTOR = + By.enabled(true) + .checkable(false) + .clickable(true) + .text(NEXT_BTN_PATTERN); + + /** + * Buttons with content description matching {@link #NEXT_BTN_PATTERN}. + */ + private static final BySelector NAVIGATION_BTN_DESC_SELECTOR = + By.enabled(true) + .checkable(false) + .clickable(true) + .desc(NEXT_BTN_PATTERN); + + /** + * List of package names whose buttons should not be clicked. + */ + private Set<String> mPackageNameBlacklist = null; + + /** + * Stop skipping when any object with this {@link BySelector} is found. + */ + private final BySelector mSkipEndSelector; + + /** + * Creates a new page skipper. + * + * @param uiDevice {@link UiDevice} object to access UIAutomator API + * @param skipEndSelector stop skipping pages if this any object matching this + * {@link BySelector} is found + * @param testConfig {@link testConfig} to get test configurations + */ + public PageSkipper(UiDevice uiDevice, BySelector skipEndSelector, TestConfig testConfig) { + super(uiDevice, testConfig); + + mSkipEndSelector = skipEndSelector; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean waitForLoading() throws Exception { + // Do nothing. + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public BySelector uniqueElement() { + // No unique element. + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void navigate() throws Exception { + long timeout = TimeUnit.MINUTES.toMillis(1) * getTestConfig().getTimeoutSize(); + Timer timer = new Timer(timeout); + List<OemWidget> oemWidgets = getTestConfig().getOemWidgets(); + timer.start(); + while (!timer.isTimeUp() && !getUiDevice().hasObject(mSkipEndSelector)) { + + if (!clickAvailableNextButton()) { + iterateOemWidgets(oemWidgets); + } + + // Assert on fatal app crash + assertOnFatalAppCrash(); + + SystemClock.sleep(THREAD_SLEEP_TIME_MS); + } + + if (timer.isTimeUp()) { + Log.e(TAG, "Timeout"); + } + } + + /** + * Finds buttons with text or content description that match pattern {@link NEXT_BTN_PATTERN} + * and click on them. + * + * @return {@code true} if any button found, {@code false} otherwise + */ + private boolean clickAvailableNextButton() { + return clickAnyButton(getUiDevice().findObjects(NAVIGATION_BTN_TEXT_SELECTOR)) + || clickAnyButton(getUiDevice().findObjects(NAVIGATION_BTN_DESC_SELECTOR)); + } + + /** + * Clicks any button from given list. + * + * @param buttons list of buttons to click + * @return {@code true} if there is any button clicked successfully, {@code false} otherwise + */ + private boolean clickAnyButton(List<UiObject2> navigationBtns) { + for (UiObject2 obj : navigationBtns) { + // Skip widget with package name in mPackageNameBlacklist. + String pkgName = WidgetUtils.getPackageName(obj); + if (mPackageNameBlacklist != null && mPackageNameBlacklist.contains(pkgName)) { + continue; + } + + if (safeClick(obj)) { + // Returns immediately after the first 'Next' button is found and clicked; because + // after clicking the current view hierarchy will change; the found navigation + // buttons will be stale. + return true; + } + } + + return false; + } + + /** + * Iterates OEM widgets and act on found ones if necessary. + * + * @param oemWidgets {@link List} of {@link OemWidget} to operate + */ + private void iterateOemWidgets(List<OemWidget> oemWidgets) { + + for (OemWidget widget : oemWidgets) { + BySelector selector = BySelectorHelper.getSelector(widget); + + if (getUiDevice().hasObject(selector)) { + Log.d(TAG, "Found OEM widget: " + widget.toString()); + + if (widget.getAction().equals(ACTION_CHECK)) { + safeClick(getUiDevice().findObject(selector)); + } else if (widget.getAction().equals(ACTION_SCROLL)){ + safeFling( + getUiDevice().findObject(selector), + Direction.valueOf(widget.getScrollDirection())); + } else if (widget.getAction().equals(ACTION_CLICK)) { + safeClick(getUiDevice().findObject(selector)); + } + } + } + } + + /** + * Sets list of package names which should not be skipped. + * + * @param blacklist list of package names + */ + public void setPackageNameBlacklist(Set<String> blacklist) { + mPackageNameBlacklist = blacklist; + } +} |