Simplify mobile page objects and stabilize ID-based components

This commit is contained in:
2026-04-19 12:17:15 +03:00
parent 46f3de4d55
commit bcf831ad99
19 changed files with 416 additions and 246 deletions
@@ -1,25 +1,27 @@
package ru.otus.mobile.component; package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.config.MobileConfig; import io.appium.java_client.AppiumBy;
import static com.codeborne.selenide.appium.SelenideAppium.$;
public final class AlertDialogComponent extends BaseMobileComponent { public final class AlertDialogComponent extends BaseMobileComponent {
private final SelenideElement positiveButton;
@Inject @Inject
public AlertDialogComponent(MobileConfig config) { public AlertDialogComponent() {
super(config); this($(AppiumBy.id("android:id/content")));
}
public AlertDialogComponent(SelenideElement root) {
super(root);
this.positiveButton = byIdInRoot("android:id/button1");
} }
public void acceptIfVisible() { public void acceptIfVisible() {
if (exists("android:id/button1")) { if (positiveButton.exists()) {
tap("android:id/button1"); positiveButton.click();
return;
}
if (byText("OK").exists()) {
byText("OK").click();
return;
}
if (byText("ОК").exists()) {
byText("ОК").click();
} }
} }
} }
@@ -1,10 +1,22 @@
package ru.otus.mobile.component; package ru.otus.mobile.component;
import ru.otus.mobile.config.MobileConfig; import com.codeborne.selenide.ElementsCollection;
import com.codeborne.selenide.SelenideElement;
import io.appium.java_client.AppiumBy;
import ru.otus.mobile.page.AbsPageObject; import ru.otus.mobile.page.AbsPageObject;
public abstract class BaseMobileComponent extends AbsPageObject { public abstract class BaseMobileComponent extends AbsPageObject {
protected BaseMobileComponent(MobileConfig config) { protected final SelenideElement root;
super(config);
protected BaseMobileComponent(SelenideElement root) {
this.root = root;
}
protected SelenideElement byIdInRoot(String id) {
return root.$(AppiumBy.id(fullIdValue(id)));
}
protected ElementsCollection allByIdInRoot(String id) {
return root.$$(AppiumBy.id(fullIdValue(id)));
} }
} }
@@ -1,19 +1,31 @@
package ru.otus.mobile.component; package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.config.MobileConfig; import io.appium.java_client.AppiumBy;
import static com.codeborne.selenide.appium.SelenideAppium.$;
public final class BottomNavigationComponent extends BaseMobileComponent { public final class BottomNavigationComponent extends BaseMobileComponent {
private final SelenideElement mineMenu;
private final SelenideElement usersMenu;
@Inject @Inject
public BottomNavigationComponent(MobileConfig config) { public BottomNavigationComponent() {
super(config); this($(AppiumBy.id(fullIdValue("bottom_navigation"))));
}
public BottomNavigationComponent(SelenideElement root) {
super(root);
this.mineMenu = byIdInRoot("mine_menu");
this.usersMenu = byIdInRoot("users_menu");
} }
public void openWishlists() { public void openWishlists() {
tap("mine_menu"); mineMenu.click();
} }
public void openUsers() { public void openUsers() {
tap("users_menu"); usersMenu.click();
} }
} }
@@ -1,17 +1,35 @@
package ru.otus.mobile.component; package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.config.MobileConfig; import io.appium.java_client.AppiumBy;
import static com.codeborne.selenide.appium.SelenideAppium.$;
public final class GiftFormComponent extends BaseMobileComponent { public final class GiftFormComponent extends BaseMobileComponent {
private final SelenideElement nameInput;
private final SelenideElement priceInput;
private final SelenideElement saveButton;
@Inject @Inject
public GiftFormComponent(MobileConfig config) { public GiftFormComponent() {
super(config); this($(AppiumBy.id("android:id/content")));
}
public GiftFormComponent(SelenideElement root) {
super(root);
this.nameInput = byIdInRoot("name_input");
this.priceInput = byIdInRoot("price_input");
this.saveButton = byIdInRoot("save_button");
} }
public void save(String name, String price) { public void save(String name, String price) {
type("name_input", name); nameInput.click();
type("price_input", price); nameInput.clear();
tap("save_button"); nameInput.sendKeys(name);
priceInput.click();
priceInput.clear();
priceInput.sendKeys(price);
saveButton.click();
} }
} }
@@ -0,0 +1,32 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import static com.codeborne.selenide.Condition.text;
public final class GiftItemComponent extends BaseMobileComponent {
private final SelenideElement title;
private final SelenideElement editButton;
public GiftItemComponent(SelenideElement root) {
super(root);
this.title = byIdInRoot("title");
this.editButton = byIdInRoot("edit_button");
}
public String titleText() {
return title.getText();
}
public void shouldHaveTitle(String value) {
title.shouldHave(text(value));
}
public void open() {
title.click();
}
public void edit() {
editButton.click();
}
}
@@ -0,0 +1,32 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.ElementsCollection;
import com.codeborne.selenide.SelenideElement;
import io.appium.java_client.AppiumBy;
public final class GiftsContentComponent extends BaseMobileComponent {
private final ElementsCollection items;
public GiftsContentComponent(SelenideElement root) {
super(root);
this.items = root.$$(AppiumBy.id(fullIdValue("gift_item")));
}
public GiftItemComponent get(int index) {
return new GiftItemComponent(items.get(index));
}
public GiftItemComponent first() {
return get(0);
}
public GiftItemComponent byTitle(String title) {
for (int i = 0; i < items.size(); i++) {
GiftItemComponent item = get(i);
if (title.equals(item.titleText())) {
return item;
}
}
throw new IllegalStateException("Gift with title '" + title + "' was not found.");
}
}
@@ -0,0 +1,29 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
public final class TopBarComponent extends BaseMobileComponent {
public TopBarComponent(SelenideElement root) {
super(root);
}
public void openUsersFilter() {
if (byId("filter").exists()) {
byId("filter").click();
return;
}
if (byId("users_filter").exists()) {
byId("users_filter").click();
return;
}
if (byId("filter_button").exists()) {
byId("filter_button").click();
return;
}
if (byId("action_filter").exists()) {
byId("action_filter").click();
return;
}
throw new IllegalStateException("Filter button was not found on Users screen.");
}
}
@@ -0,0 +1,20 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
public final class UserItemComponent extends BaseMobileComponent {
private final SelenideElement username;
public UserItemComponent(SelenideElement root) {
super(root);
this.username = byIdInRoot("username");
}
public String usernameText() {
return username.getText();
}
public void open() {
username.click();
}
}
@@ -0,0 +1,32 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.ElementsCollection;
import com.codeborne.selenide.SelenideElement;
import io.appium.java_client.AppiumBy;
public final class UsersContentComponent extends BaseMobileComponent {
private final ElementsCollection items;
public UsersContentComponent(SelenideElement root) {
super(root);
this.items = root.$$(AppiumBy.id(fullIdValue("user_item")));
}
public UserItemComponent get(int index) {
return new UserItemComponent(items.get(index));
}
public UserItemComponent first() {
return get(0);
}
public UserItemComponent byUsername(String username) {
for (int i = 0; i < items.size(); i++) {
UserItemComponent item = get(i);
if (username.equals(item.usernameText())) {
return item;
}
}
throw new IllegalStateException("User with username '" + username + "' was not found.");
}
}
@@ -0,0 +1,38 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
public final class UsersFilterComponent extends BaseMobileComponent {
public UsersFilterComponent(SelenideElement root) {
super(root);
}
public void applyByUsername(String username) {
SelenideElement filterInput = filterInput();
SelenideElement applyButton = applyButton();
filterInput.click();
filterInput.clear();
filterInput.sendKeys(username);
applyButton.click();
}
private SelenideElement filterInput() {
if (byIdInRoot("username_input").exists()) {
return byIdInRoot("username_input");
}
if (byIdInRoot("filter_input").exists()) {
return byIdInRoot("filter_input");
}
if (byIdInRoot("users_filter_input").exists()) {
return byIdInRoot("users_filter_input");
}
return byIdInRoot("username_filter_input");
}
private SelenideElement applyButton() {
if (byIdInRoot("apply_button").exists()) {
return byIdInRoot("apply_button");
}
return byIdInRoot("apply");
}
}
@@ -1,16 +1,30 @@
package ru.otus.mobile.component; package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.config.MobileConfig; import io.appium.java_client.AppiumBy;
import static com.codeborne.selenide.appium.SelenideAppium.$;
public final class WishlistFormComponent extends BaseMobileComponent { public final class WishlistFormComponent extends BaseMobileComponent {
private final SelenideElement titleInput;
private final SelenideElement saveButton;
@Inject @Inject
public WishlistFormComponent(MobileConfig config) { public WishlistFormComponent() {
super(config); this($(AppiumBy.id("android:id/content")));
}
public WishlistFormComponent(SelenideElement root) {
super(root);
this.titleInput = byIdInRoot("title_input");
this.saveButton = byIdInRoot("save_button");
} }
public void save(String title) { public void save(String title) {
type("title_input", title); titleInput.click();
tap("save_button"); titleInput.clear();
titleInput.sendKeys(title);
saveButton.click();
} }
} }
@@ -0,0 +1,32 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.SelenideElement;
import static com.codeborne.selenide.Condition.text;
public final class WishlistItemComponent extends BaseMobileComponent {
private final SelenideElement title;
private final SelenideElement editButton;
public WishlistItemComponent(SelenideElement root) {
super(root);
this.title = byIdInRoot("title");
this.editButton = byIdInRoot("edit_button");
}
public String titleText() {
return title.getText();
}
public void shouldHaveTitle(String value) {
title.shouldHave(text(value));
}
public void open() {
title.click();
}
public void edit() {
editButton.click();
}
}
@@ -0,0 +1,32 @@
package ru.otus.mobile.component;
import com.codeborne.selenide.ElementsCollection;
import com.codeborne.selenide.SelenideElement;
import io.appium.java_client.AppiumBy;
public final class WishlistsContentComponent extends BaseMobileComponent {
private final ElementsCollection items;
public WishlistsContentComponent(SelenideElement root) {
super(root);
this.items = root.$$(AppiumBy.id(fullIdValue("wishlist_item")));
}
public WishlistItemComponent get(int index) {
return new WishlistItemComponent(items.get(index));
}
public WishlistItemComponent first() {
return get(0);
}
public WishlistItemComponent byTitle(String title) {
for (int i = 0; i < items.size(); i++) {
WishlistItemComponent item = get(i);
if (title.equals(item.titleText())) {
return item;
}
}
throw new IllegalStateException("Wishlist with title '" + title + "' was not found.");
}
}
@@ -1,19 +1,11 @@
package ru.otus.mobile.page; package ru.otus.mobile.page;
import ru.otus.mobile.component.BottomNavigationComponent; import ru.otus.mobile.component.BottomNavigationComponent;
import ru.otus.mobile.config.MobileConfig;
public abstract class AbsBasePage extends AbsPageObject { public abstract class AbsBasePage extends AbsPageObject {
protected final BottomNavigationComponent bottomNavigation; protected final BottomNavigationComponent bottomNavigation;
private final MobileConfig config;
protected AbsBasePage(MobileConfig config, BottomNavigationComponent bottomNavigation) { protected AbsBasePage(BottomNavigationComponent bottomNavigation) {
super(config);
this.config = config;
this.bottomNavigation = bottomNavigation; this.bottomNavigation = bottomNavigation;
} }
protected MobileConfig config() {
return config;
}
} }
@@ -1,45 +1,24 @@
package ru.otus.mobile.page; package ru.otus.mobile.page;
import com.codeborne.selenide.CollectionCondition;
import com.codeborne.selenide.Condition; import com.codeborne.selenide.Condition;
import com.codeborne.selenide.SelenideElement; import com.codeborne.selenide.SelenideElement;
import com.codeborne.selenide.WebDriverRunner; import com.codeborne.selenide.WebDriverRunner;
import com.codeborne.selenide.appium.SelenideAppiumCollection; import com.codeborne.selenide.appium.SelenideAppiumCollection;
import io.appium.java_client.AppiumBy; import io.appium.java_client.AppiumBy;
import ru.otus.mobile.config.MobileConfig;
import static com.codeborne.selenide.appium.SelenideAppium.$$; import static com.codeborne.selenide.appium.SelenideAppium.$$;
import static com.codeborne.selenide.appium.SelenideAppium.$; import static com.codeborne.selenide.appium.SelenideAppium.$;
public abstract class AbsPageObject { public abstract class AbsPageObject {
private final MobileConfig config; private static final String APP_PACKAGE_PROPERTY = "app.package";
private static final String DEFAULT_APP_PACKAGE = "ru.otus.wishlist";
protected AbsPageObject(MobileConfig config) {
this.config = config;
}
protected SelenideElement byId(String id) { protected SelenideElement byId(String id) {
return $(AppiumBy.id(fullId(id))); return $(AppiumBy.id(fullIdValue(id)));
} }
protected SelenideAppiumCollection allById(String id) { protected SelenideAppiumCollection allById(String id) {
return $$(AppiumBy.id(fullId(id))); return $$(AppiumBy.id(fullIdValue(id)));
}
protected SelenideElement byText(String text) {
return $(AppiumBy.androidUIAutomator("new UiSelector().text(\"" + escape(text) + "\")"));
}
protected SelenideElement byTextContains(String text) {
return $(AppiumBy.androidUIAutomator("new UiSelector().textContains(\"" + escape(text) + "\")"));
}
protected SelenideElement byUiAutomator(String selector) {
return $(AppiumBy.androidUIAutomator(selector));
}
protected SelenideElement byXpath(String xpath) {
return $(AppiumBy.xpath(xpath));
} }
protected void type(String id, String value) { protected void type(String id, String value) {
@@ -57,47 +36,15 @@ public abstract class AbsPageObject {
return byId(id).exists(); return byId(id).exists();
} }
protected void shouldHaveItems(String id) {
allById(id).shouldHave(CollectionCondition.sizeGreaterThan(0));
}
protected SelenideElement scrollToText(String text) {
String selector = "new UiScrollable(new UiSelector().scrollable(true))"
+ ".scrollIntoView(new UiSelector().textContains(\"" + escape(text) + "\"))";
return $(AppiumBy.androidUIAutomator(selector));
}
protected void back() { protected void back() {
WebDriverRunner.getWebDriver().navigate().back(); WebDriverRunner.getWebDriver().navigate().back();
} }
protected String xpathLiteral(String value) { protected static String fullIdValue(String id) {
if (!value.contains("\"")) {
return "\"" + value + "\"";
}
if (!value.contains("'")) {
return "'" + value + "'";
}
String[] parts = value.split("\"");
StringBuilder builder = new StringBuilder("concat(");
for (int i = 0; i < parts.length; i++) {
if (i > 0) {
builder.append(", '\"', ");
}
builder.append("\"").append(parts[i]).append("\"");
}
builder.append(")");
return builder.toString();
}
private String fullId(String id) {
if (id.contains(":")) { if (id.contains(":")) {
return id; return id;
} }
return config.appPackage() + ":id/" + id; String appPackage = System.getProperty(APP_PACKAGE_PROPERTY, DEFAULT_APP_PACKAGE);
} return appPackage + ":id/" + id;
private String escape(String value) {
return value.replace("\"", "\\\"");
} }
} }
@@ -3,34 +3,39 @@ package ru.otus.mobile.page;
import com.codeborne.selenide.Condition; import com.codeborne.selenide.Condition;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.component.BottomNavigationComponent; import ru.otus.mobile.component.BottomNavigationComponent;
import ru.otus.mobile.component.GiftsContentComponent;
import ru.otus.mobile.component.GiftFormComponent; import ru.otus.mobile.component.GiftFormComponent;
import ru.otus.mobile.config.MobileConfig;
public final class GiftsPage extends AbsBasePage { public final class GiftsPage extends AbsBasePage {
private final GiftFormComponent form; private final GiftFormComponent form;
private final com.codeborne.selenide.SelenideElement addButton = byId("add_button");
@Inject @Inject
public GiftsPage(MobileConfig config, BottomNavigationComponent bottomNavigation, GiftFormComponent form) { public GiftsPage(BottomNavigationComponent bottomNavigation, GiftFormComponent form) {
super(config, bottomNavigation); super(bottomNavigation);
this.form = form; this.form = form;
} }
public void createGift(String name) { public void createGift(String name) {
tap("add_button"); addButton.click();
form.save(name, "100"); form.save(name, "100");
} }
public void editGift(String oldName, String newName) { public void editGift(String oldName, String newName) {
openGift(oldName); openGift(oldName);
tap("edit_button"); content().byTitle(oldName).edit();
form.save(newName, "100"); form.save(newName, "100");
} }
public void shouldSeeGift(String name) { public void shouldSeeGift(String name) {
scrollToText(name).shouldBe(Condition.visible); content().byTitle(name).shouldHaveTitle(name);
} }
private void openGift(String name) { private void openGift(String name) {
scrollToText(name).click(); content().byTitle(name).open();
}
private GiftsContentComponent content() {
return new GiftsContentComponent(byId("gifts_content"));
} }
} }
@@ -2,15 +2,13 @@ package ru.otus.mobile.page;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.component.AlertDialogComponent; import ru.otus.mobile.component.AlertDialogComponent;
import ru.otus.mobile.config.MobileConfig;
import ru.otus.mobile.config.TestAccount; import ru.otus.mobile.config.TestAccount;
public final class LoginPage extends AbsPageObject { public final class LoginPage extends AbsPageObject {
private final AlertDialogComponent alertDialog; private final AlertDialogComponent alertDialog;
@Inject @Inject
public LoginPage(MobileConfig config, AlertDialogComponent alertDialog) { public LoginPage(AlertDialogComponent alertDialog) {
super(config);
this.alertDialog = alertDialog; this.alertDialog = alertDialog;
} }
+35 -107
View File
@@ -1,69 +1,55 @@
package ru.otus.mobile.page; package ru.otus.mobile.page;
import com.codeborne.selenide.Condition; import com.codeborne.selenide.Condition;
import com.codeborne.selenide.CollectionCondition;
import com.codeborne.selenide.SelenideElement;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.component.BottomNavigationComponent; import ru.otus.mobile.component.BottomNavigationComponent;
import ru.otus.mobile.config.MobileConfig; import ru.otus.mobile.component.GiftsContentComponent;
import ru.otus.mobile.component.TopBarComponent;
import ru.otus.mobile.component.UsersContentComponent;
import ru.otus.mobile.component.UsersFilterComponent;
import ru.otus.mobile.component.WishlistsContentComponent;
import java.time.Duration; import java.time.Duration;
public final class UsersPage extends AbsBasePage { public final class UsersPage extends AbsBasePage {
private final TopBarComponent topBar;
@Inject @Inject
public UsersPage(MobileConfig config, BottomNavigationComponent bottomNavigation) { public UsersPage(BottomNavigationComponent bottomNavigation) {
super(config, bottomNavigation); super(bottomNavigation);
this.topBar = new TopBarComponent(byId("android:id/content"));
} }
public void open() { public void open() {
bottomNavigation.openUsers(); bottomNavigation.openUsers();
byId("users_content").shouldBe(Condition.visible); usersContentRoot().shouldBe(Condition.visible, Duration.ofSeconds(15));
} }
public void filterByUsername(String username) { public void filterByUsername(String username) {
openFilter(); topBar.openUsersFilter();
SelenideElement input = filterInput().shouldBe(Condition.visible, Duration.ofSeconds(15)); byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
input.clear(); new UsersFilterComponent(byId("users_filter_bottom_sheet")).applyByUsername(username);
input.setValue(username); byId("user_item").shouldBe(Condition.visible, Duration.ofSeconds(15));
applyFilter();
byTextContains(username).shouldBe(Condition.visible, Duration.ofSeconds(15));
} }
public void openUser(String username) { public void openUser(String username) {
String appPackage = config().appPackage(); usersContent().byUsername(username).open();
String xpath = "//android.widget.TextView[@resource-id='" + appPackage + ":id/username'"
+ " and @text=" + xpathLiteral(username) + "]";
SelenideElement user = byXpath(xpath);
if (user.exists()) {
user.shouldBe(Condition.visible, Duration.ofSeconds(15)).click();
} else {
scrollToText(username).shouldBe(Condition.visible, Duration.ofSeconds(15)).click();
}
byTextContains(username).shouldBe(Condition.visible, Duration.ofSeconds(15));
} }
public void openFirstWishlist() { public void openFirstWishlist() {
allById("wishlist_item") wishlistsContent().get(0).open();
.shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15))
.first()
.shouldBe(Condition.visible, Duration.ofSeconds(15))
.click();
} }
public void openWishlist(String name) { public void openWishlist(String name) {
scrollToText(name).shouldBe(Condition.visible, Duration.ofSeconds(15)).click(); wishlistsContent().byTitle(name).open();
} }
public void openFirstGift() { public void openFirstGift() {
allById("gift_item") new GiftsContentComponent(byId("gifts_content")).get(0).open();
.shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15))
.first()
.shouldBe(Condition.visible, Duration.ofSeconds(15))
.click();
} }
public void openGift(String name) { public void openGift(String name) {
scrollToText(name).shouldBe(Condition.visible, Duration.ofSeconds(15)).click(); new GiftsContentComponent(byId("gifts_content")).byTitle(name).open();
} }
public boolean isReserved() { public boolean isReserved() {
@@ -71,87 +57,29 @@ public final class UsersPage extends AbsBasePage {
} }
public void toggleReservation() { public void toggleReservation() {
tap("reserved"); byId("reserved").shouldBe(Condition.visible).click();
} }
private void openFilter() { private UsersContentComponent usersContent() {
if (byId("users_filter").exists()) { return new UsersContentComponent(usersContentRoot());
tap("users_filter");
byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
return;
}
if (byId("filter_button").exists()) {
tap("filter_button");
byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
return;
}
if (byId("action_filter").exists()) {
tap("action_filter");
byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
return;
}
SelenideElement button = byUiAutomator("new UiSelector().descriptionContains(\"Фильтр\")");
if (button.exists()) {
button.click();
byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
return;
}
button = byUiAutomator("new UiSelector().descriptionContains(\"Filter\")");
if (button.exists()) {
button.click();
byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15));
return;
}
throw new IllegalStateException("Filter button was not found on Users screen.");
} }
private SelenideElement filterInput() { private WishlistsContentComponent wishlistsContent() {
if (byId("filter_input").exists()) { return new WishlistsContentComponent(wishlistsContentRoot());
return byId("filter_input");
}
if (byId("username_filter_input").exists()) {
return byId("username_filter_input");
}
if (byId("users_filter_input").exists()) {
return byId("users_filter_input");
}
if (byId("username_input").exists()) {
return byId("username_input");
}
if (byId("search_src_text").exists()) {
return byId("search_src_text");
}
if (byId("query_text").exists()) {
return byId("query_text");
}
String appPackage = config().appPackage();
SelenideElement editText = byXpath(
"//android.view.ViewGroup[@resource-id='" + appPackage + ":id/users_filter_bottom_sheet']"
+ "//android.widget.EditText"
);
if (editText.exists()) {
return editText;
}
throw new IllegalStateException("Filter input was not found on Users screen.");
} }
private void applyFilter() { private com.codeborne.selenide.SelenideElement usersContentRoot() {
if (byId("apply_button").exists()) { if (byId("users_content").exists()) {
tap("apply_button"); return byId("users_content");
return;
} }
if (byId("apply").exists()) { return byId("users");
tap("apply");
return;
} }
if (byText("ПРИМЕНИТЬ").exists()) {
byText("ПРИМЕНИТЬ").click(); private com.codeborne.selenide.SelenideElement wishlistsContentRoot() {
return; if (byId("wishlists_content").exists()) {
return byId("wishlists_content");
} }
if (byText("APPLY").exists()) { return byId("wishlists");
byText("APPLY").click();
return;
}
throw new IllegalStateException("Apply filter button was not found on Users filter screen.");
} }
} }
@@ -1,72 +1,67 @@
package ru.otus.mobile.page; package ru.otus.mobile.page;
import com.codeborne.selenide.Condition; import com.codeborne.selenide.Condition;
import com.codeborne.selenide.CollectionCondition;
import com.google.inject.Inject; import com.google.inject.Inject;
import ru.otus.mobile.component.BottomNavigationComponent; import ru.otus.mobile.component.BottomNavigationComponent;
import ru.otus.mobile.component.WishlistsContentComponent;
import ru.otus.mobile.component.WishlistFormComponent; import ru.otus.mobile.component.WishlistFormComponent;
import ru.otus.mobile.config.MobileConfig;
import java.time.Duration;
public final class WishlistsPage extends AbsBasePage { public final class WishlistsPage extends AbsBasePage {
private final WishlistFormComponent form; private final WishlistFormComponent form;
private final com.codeborne.selenide.SelenideElement addButton = byId("add_button");
@Inject @Inject
public WishlistsPage(MobileConfig config, BottomNavigationComponent bottomNavigation, WishlistFormComponent form) { public WishlistsPage(BottomNavigationComponent bottomNavigation, WishlistFormComponent form) {
super(config, bottomNavigation); super(bottomNavigation);
this.form = form; this.form = form;
} }
public void open() { public void open() {
bottomNavigation.openWishlists(); bottomNavigation.openWishlists();
byId("wishlists_content").shouldBe(Condition.visible); contentRoot().shouldBe(Condition.visible);
} }
public void createWishlist(String name) { public void createWishlist(String name) {
tap("add_button"); addButton.click();
form.save(name); form.save(name);
} }
public void editWishlist(String oldName, String newName) { public void editWishlist(String oldName, String newName) {
ensureWishlistsList(); ensureWishlistsList();
editButtonFor(oldName).shouldBe(Condition.visible).click(); content().byTitle(oldName).edit();
form.save(newName); form.save(newName);
} }
public void shouldSeeWishlist(String name) { public void shouldSeeWishlist(String name) {
scrollToText(name).shouldBe(Condition.visible); content().byTitle(name).shouldHaveTitle(name);
} }
public void openWishlist(String name) { public void openWishlist(String name) {
scrollToText(name).click(); content().byTitle(name).open();
} }
public void openFirstWishlist() { public void openFirstWishlist() {
allById("wishlist_item") content().first().open();
.shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15))
.first()
.shouldBe(Condition.visible, Duration.ofSeconds(15))
.click();
} }
private void ensureWishlistsList() { private void ensureWishlistsList() {
if (exists("wishlist_item") || exists("wishlists")) { if (exists("wishlist_item") || contentRoot().exists()) {
return; return;
} }
if (exists("gifts_content") || exists("gift_item") || exists("add_button")) { if (exists("gifts_content") || exists("gift_item") || exists("add_button")) {
back(); back();
} }
byId("wishlists_content").shouldBe(Condition.visible); contentRoot().shouldBe(Condition.visible);
} }
private com.codeborne.selenide.SelenideElement editButtonFor(String name) { private WishlistsContentComponent content() {
String full = config().appPackage(); return new WishlistsContentComponent(contentRoot());
String xpath = "//androidx.recyclerview.widget.RecyclerView[@resource-id='" + full + ":id/wishlists']" }
+ "//android.view.ViewGroup[@resource-id='" + full + ":id/wishlist_item'"
+ " and .//android.widget.TextView[@resource-id='" + full + ":id/title'" private com.codeborne.selenide.SelenideElement contentRoot() {
+ " and contains(@text," + xpathLiteral(name) + ")]]" if (byId("wishlists_content").exists()) {
+ "//android.widget.Button[@resource-id='" + full + ":id/edit_button']"; return byId("wishlists_content");
return byXpath(xpath); }
return byId("wishlists");
} }
} }