Commit 1eaedfac authored by Andrew Luo's avatar Andrew Luo Committed by Commit Bot

Add Feed/Zine Controllers.

Bug: 924194
Change-Id: I51fa3da0276d8cf15a7357369d58e5e9912702d2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1683671
Commit-Queue: Andrew Luo <aluo@chromium.org>
Reviewed-by: default avatarJohn Budorick <jbudorick@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#680057}
parent 7217b543
......@@ -13,6 +13,8 @@ android_library("chrome_java_test_pagecontroller") {
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/SyncController.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/first_run/TOSController.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/notifications/DownloadNotificationController.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleActionsMenu.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ArticleCardController.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/ChromeMenu.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/IncognitoNewTabPageController.java",
"javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java",
......@@ -38,6 +40,7 @@ android_library("chrome_java_test_pagecontroller") {
"//base:base_java_test_support",
"//chrome/android:chrome_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/guava:guava_android_java",
"//third_party/junit",
"//third_party/ub-uiautomator:ub_uiautomator_java",
]
......
// Copyright 2019 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.test.pagecontroller.controllers.ntp;
import com.google.android.libraries.feed.sharedstream.contextmenumanager.R;
import org.chromium.chrome.test.pagecontroller.controllers.PageController;
import org.chromium.chrome.test.pagecontroller.controllers.urlpage.UrlPage;
import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
/**
* Article Actions Menu (long-press on NTP article) Page Controller.
*/
public class ArticleActionsMenu extends PageController {
private static final IUi2Locator LOCATOR_MENU = Ui2Locators.withClassRegex(".*ListView");
private static final IUi2Locator LOCATOR_OPEN_NEW_TAB = Ui2Locators.withResEntriesByIndex(
0, org.chromium.chrome.R.id.title, R.id.feed_simple_list_item);
private static final IUi2Locator LOCATOR_OPEN_INCOGNITO = Ui2Locators.withResEntriesByIndex(
1, org.chromium.chrome.R.id.title, R.id.feed_simple_list_item);
private static final IUi2Locator LOCATOR_DOWNLOAD_LINK = Ui2Locators.withResEntriesByIndex(
2, org.chromium.chrome.R.id.title, R.id.feed_simple_list_item);
private static final IUi2Locator LOCATOR_REMOVE = Ui2Locators.withResEntriesByIndex(
3, org.chromium.chrome.R.id.title, R.id.feed_simple_list_item);
private static final IUi2Locator LOCATOR_LEARN_MORE = Ui2Locators.withResEntriesByIndex(
4, org.chromium.chrome.R.id.title, R.id.feed_simple_list_item);
static private ArticleActionsMenu sInstance = new ArticleActionsMenu();
private ArticleActionsMenu() {}
static public ArticleActionsMenu getInstance() {
return sInstance;
}
@Override
public boolean isCurrentPageThis() {
return mLocatorHelper.isOnScreen(LOCATOR_LEARN_MORE);
}
public UrlPage clickOpenNewTab() {
mUtils.click(LOCATOR_OPEN_NEW_TAB);
UrlPage inst = UrlPage.getInstance();
inst.verify();
return inst;
}
public UrlPage clickOpenIncognitoTab() {
mUtils.click(LOCATOR_OPEN_INCOGNITO);
UrlPage inst = UrlPage.getInstance();
inst.verify();
return inst;
}
public void clickDownloadLink() {
mUtils.click(LOCATOR_DOWNLOAD_LINK);
}
public NewTabPageController clickRemoveArticle() {
mUtils.click(LOCATOR_REMOVE);
NewTabPageController inst = NewTabPageController.getInstance();
inst.verify();
return inst;
}
public void clickLearnMore() {
mUtils.click(LOCATOR_LEARN_MORE);
}
public NewTabPageController dismiss() {
mUtils.clickOutsideOf(LOCATOR_MENU);
NewTabPageController inst = NewTabPageController.getInstance();
inst.verify();
return inst;
}
}
// Copyright 2019 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.test.pagecontroller.controllers.ntp;
import static org.chromium.chrome.test.pagecontroller.utils.Ui2Locators.withText;
import static org.chromium.chrome.test.pagecontroller.utils.Ui2Locators.withTextRegex;
import android.support.test.uiautomator.UiObject2;
import com.google.common.base.Joiner;
import org.chromium.chrome.R;
import org.chromium.chrome.test.pagecontroller.controllers.ElementController;
import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
import org.chromium.chrome.test.pagecontroller.utils.UiLocationException;
import org.chromium.chrome.test.pagecontroller.utils.UiLocatorHelper;
import java.util.List;
import java.util.Objects;
/**
* NTP Article Element Controller.
*/
public class ArticleCardController extends ElementController {
// Implementation type of the article card.
public enum ImplementationType { ZINE, FEED };
/**
* Represents a single article, can be used by the NewTabPageController
* to perform actions.
*/
static public class Info {
private final String mHeadline, mPublisher, mAge;
private final ImplementationType mImplType;
public Info(String headline, String publisher, String age,
ImplementationType implementationType) {
mHeadline = headline;
mPublisher = publisher;
mAge = age;
mImplType = implementationType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Info)) return false;
Info that = (Info) o;
return Objects.equals(mHeadline, that.mHeadline)
&& Objects.equals(mPublisher, that.mPublisher)
&& Objects.equals(mAge, that.mAge) && mImplType == that.mImplType;
}
@Override
public int hashCode() {
return Objects.hash(mHeadline, mPublisher, mAge, mImplType);
}
@Override
public String toString() {
return "ArticleCardController{"
+ "mHeadline='" + mHeadline + '\'' + ", mPublisher='" + mPublisher + '\''
+ ", mAge='" + mAge + '\'' + ", mImplementation=" + mImplType + '}';
}
public String getHeadline() {
return mHeadline;
}
public String getPublisher() {
return mPublisher;
}
public String getAge() {
return mAge;
}
public ImplementationType getImplementationType() {
return mImplType;
}
}
private static abstract class ArticleImpl {
abstract public List<Info> parseScreenForArticles(UiLocatorHelper locatorHelper);
abstract public IUi2Locator getLocator(Info cardInfo);
protected List<Info> parseScreenForArticles(final UiLocatorHelper locatorHelper,
final ImplementationType implementationType, final IUi2Locator cardsLocator,
final IUi2Locator headlineLocator, final IUi2Locator publisherLocator,
final IUi2Locator ageLocator) {
return locatorHelper.getCustomElements(
cardsLocator, new UiLocatorHelper.CustomElementMaker<Info>() {
@Override
public Info makeElement(UiObject2 articleCard, boolean isLastAttempt) {
String headline =
locatorHelper.getOneTextImmediate(headlineLocator, articleCard);
String publisher = locatorHelper.getOneTextImmediate(
publisherLocator, articleCard);
String age = locatorHelper.getOneTextImmediate(ageLocator, articleCard);
if (headline != null && publisher != null && age != null) {
return new Info(headline, publisher, age, implementationType);
} else if (isLastAttempt) {
return null;
} else {
if (headline == null)
throw UiLocationException.newInstance(
"Headline not found", headlineLocator, articleCard);
else if (publisher == null)
throw UiLocationException.newInstance(
"Publisher not found", publisherLocator, articleCard);
else
throw UiLocationException.newInstance(
"Age not found", ageLocator, articleCard);
}
}
});
}
}
private static class FeedArticleImpl extends ArticleImpl {
private static final IUi2Locator LOCATOR_NON_EMPTY_STRING = withTextRegex(".+");
private static final IUi2Locator LOCATOR_CARDS = Ui2Locators.withPath(
Ui2Locators.withResEntries(R.id.content),
Ui2Locators.withResEntries(com.google.android.libraries.feed.basicstream.R.id
.feed_stream_recycler_view),
Ui2Locators.withResEntries(com.google.android.libraries.feed.basicstream.internal
.viewholders.R.id.feed_content_card));
private static final IUi2Locator LOCATOR_HEADLINE =
Ui2Locators.withPath(Ui2Locators.withChildIndex(0, 6), LOCATOR_NON_EMPTY_STRING);
private static final IUi2Locator LOCATOR_PUBLISHER =
Ui2Locators.withPath(Ui2Locators.withChildIndex(0, 5),
Ui2Locators.withChildIndex(1, 2), LOCATOR_NON_EMPTY_STRING);
private static final IUi2Locator LOCATOR_AGE = Ui2Locators.withPath(
Ui2Locators.withChildIndex(0, 5), Ui2Locators.withChildIndex(1),
Ui2Locators.withChildIndex(2), LOCATOR_NON_EMPTY_STRING);
@Override
public List<Info> parseScreenForArticles(UiLocatorHelper locatorHelper) {
return parseScreenForArticles(locatorHelper, ImplementationType.FEED, LOCATOR_CARDS,
LOCATOR_HEADLINE, LOCATOR_PUBLISHER, LOCATOR_AGE);
}
@Override
public IUi2Locator getLocator(Info cardInfo) {
return Ui2Locators.withPath(LOCATOR_HEADLINE, withText(cardInfo.getHeadline()));
}
}
private static class ZineArticleImpl extends ArticleImpl {
private static final IUi2Locator LOCATOR_CARDS =
Ui2Locators.withPath(Ui2Locators.withResEntries(R.id.content),
Ui2Locators.withResEntries(R.id.card_contents));
private static final IUi2Locator LOCATOR_HEADLINE =
Ui2Locators.withResEntries(R.id.article_headline);
private static final IUi2Locator LOCATOR_PUBLISHER =
Ui2Locators.withResEntries(R.id.article_publisher);
private static final IUi2Locator LOCATOR_AGE = Ui2Locators.withResEntries(R.id.article_age);
@Override
public List<Info> parseScreenForArticles(UiLocatorHelper locatorHelper) {
return parseScreenForArticles(locatorHelper, ImplementationType.ZINE, LOCATOR_CARDS,
LOCATOR_HEADLINE, LOCATOR_PUBLISHER, LOCATOR_AGE);
}
@Override
public IUi2Locator getLocator(Info cardInfo) {
return Ui2Locators.withPath(LOCATOR_HEADLINE, withText(cardInfo.getHeadline()));
}
}
private static ArticleCardController sInstance = new ArticleCardController();
private ArticleImpl mFeedImpl, mZineImpl;
private ArticleCardController() {
mFeedImpl = new FeedArticleImpl();
mZineImpl = new ZineArticleImpl();
}
public static ArticleCardController getInstance() {
return sInstance;
}
public IUi2Locator getLocator(Info cardInfo) {
return getCurrentImplementation(cardInfo.getImplementationType()).getLocator(cardInfo);
}
public List<Info> parseScreenForArticles(final ImplementationType cardImplementationType) {
return getCurrentImplementation(cardImplementationType)
.parseScreenForArticles(mLocatorHelper);
}
public String articlesToString(List<Info> cards) {
return Joiner.on("\n").join(cards);
}
private ArticleImpl getCurrentImplementation(ImplementationType implementationType) {
switch (implementationType) {
case FEED:
return mFeedImpl;
case ZINE:
return mZineImpl;
}
throw new IllegalArgumentException("Unknown implementation type" + implementationType);
}
}
......@@ -57,16 +57,26 @@ public class NewTabPageController extends PageController {
com.google.android.libraries.feed.basicstream.R.id.feed_stream_recycler_view,
R.id.card_contents);
private ArticleCardController mAriticleCardController;
private SuggestionTileController mSuggestionsTileController;
private static final NewTabPageController sInstance = new NewTabPageController();
private NewTabPageController() {
mAriticleCardController = ArticleCardController.getInstance();
mSuggestionsTileController = SuggestionTileController.getInstance();
}
public static NewTabPageController getInstance() {
return sInstance;
}
public ArticleCardController.ImplementationType getArticleImplementationType() {
if (mLocatorHelper.isOnScreen(LOCATOR_FEED_STREAM_RECYCLER_VIEW)) {
return ArticleCardController.ImplementationType.FEED;
} else {
return ArticleCardController.ImplementationType.ZINE;
}
}
/**
* Hide articles if shown, and vice-versa. This will cause page to scroll to where the
* show/hide articles button is visible.
......@@ -155,6 +165,10 @@ public class NewTabPageController extends PageController {
}
}
public List<ArticleCardController.Info> getAllLoadedArticles() {
return getAllLoadedArticles(getArticleImplementationType());
}
/**
* Get all suggestion tiles. This will cause the page to scroll to the top.
* @return List of suggestion infos, possibly empty.
......@@ -166,12 +180,53 @@ public class NewTabPageController extends PageController {
return mSuggestionsTileController.parseScreen();
}
/**
* Get all loaded articles. This will cause the page to scroll to top then down to the bottom.
* @param implementationType The article implementation type, FEED or ZINE.
* @return List of article card infos, this is a list to preserve the
* order of the articles as they appeared on the screen.
*/
public List<ArticleCardController.Info> getAllLoadedArticles(
ArticleCardController.ImplementationType implementationType) {
scrollToTop();
List<ArticleCardController.Info> allArticles =
mAriticleCardController.parseScreenForArticles(implementationType);
do {
scrollTowardsBottom(SCROLL_SWIPE_FRACTION);
List<ArticleCardController.Info> currentArticles =
mAriticleCardController.parseScreenForArticles(implementationType);
for (ArticleCardController.Info article : currentArticles) {
if (!allArticles.contains(article)) {
allArticles.add(article);
}
}
} while (!hasScrolledToBottom());
return allArticles;
}
/**
* Perform the default card action by tapping on it. This will cause the page to scroll to top
* then down to where the article is located (or hit bottom if it isn't found).
* @param article The article info.
* @return UrlPage Controller where the article will be loaded.
*/
public UrlPage clickArticle(ArticleCardController.Info article) {
scrollToTop();
IUi2Locator locator = ArticleCardController.getInstance().getLocator(article);
mUtils.swipeUpVerticallyUntilFound(locator, LOCATOR_BOTTOM_OF_PAGE);
mUtils.click(locator);
UrlPage inst = UrlPage.getInstance();
inst.verify();
return inst;
}
/**
* The default action is the one that gets performed when the user taps on the tile icon
* (opens site in a new page). If user long taps, then a menu is shown providing more choices
* (not yet implemented). This will cause the page to scroll to the top.
*/
public UrlPage performDefaultSuggestionTileAction(SuggestionTileController.Info tile) {
public UrlPage clickSuggestionTile(SuggestionTileController.Info tile) {
scrollToTop();
IUi2Locator locator = mSuggestionsTileController.getLocator(tile);
mUtils.swipeUpVerticallyUntilFound(locator, LOCATOR_BOTTOM_OF_PAGE);
......@@ -202,6 +257,16 @@ public class NewTabPageController extends PageController {
return inst;
}
public ArticleActionsMenu openArticleContextMenu(ArticleCardController.Info card) {
scrollToTop();
IUi2Locator locator = mAriticleCardController.getLocator(card);
mUtils.swipeUpVerticallyUntilFound(locator, LOCATOR_BOTTOM_OF_PAGE);
mUtils.longClick(locator);
ArticleActionsMenu inst = ArticleActionsMenu.getInstance();
inst.verify();
return inst;
}
public UrlPage omniboxSearch(String url) {
mUtils.click(LOCATOR_SEARCH_BOX_TEXT);
mUtils.setTextAndEnter(LOCATOR_URL_BAR, url);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment