From bcf831ad992fd11af876772440921473dfe5041e Mon Sep 17 00:00:00 2001 From: spawn Date: Sun, 19 Apr 2026 12:17:15 +0300 Subject: [PATCH] Simplify mobile page objects and stabilize ID-based components --- .../component/AlertDialogComponent.java | 28 ++-- .../mobile/component/BaseMobileComponent.java | 18 ++- .../component/BottomNavigationComponent.java | 22 ++- .../mobile/component/GiftFormComponent.java | 30 +++- .../mobile/component/GiftItemComponent.java | 32 ++++ .../component/GiftsContentComponent.java | 32 ++++ .../mobile/component/TopBarComponent.java | 29 ++++ .../mobile/component/UserItemComponent.java | 20 +++ .../component/UsersContentComponent.java | 32 ++++ .../component/UsersFilterComponent.java | 38 +++++ .../component/WishlistFormComponent.java | 24 ++- .../component/WishlistItemComponent.java | 32 ++++ .../component/WishlistsContentComponent.java | 32 ++++ .../java/ru/otus/mobile/page/AbsBasePage.java | 10 +- .../ru/otus/mobile/page/AbsPageObject.java | 67 +------- .../java/ru/otus/mobile/page/GiftsPage.java | 19 ++- .../java/ru/otus/mobile/page/LoginPage.java | 4 +- .../java/ru/otus/mobile/page/UsersPage.java | 146 +++++------------- .../ru/otus/mobile/page/WishlistsPage.java | 47 +++--- 19 files changed, 416 insertions(+), 246 deletions(-) create mode 100644 src/main/java/ru/otus/mobile/component/GiftItemComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/GiftsContentComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/TopBarComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/UserItemComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/UsersContentComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/UsersFilterComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/WishlistItemComponent.java create mode 100644 src/main/java/ru/otus/mobile/component/WishlistsContentComponent.java diff --git a/src/main/java/ru/otus/mobile/component/AlertDialogComponent.java b/src/main/java/ru/otus/mobile/component/AlertDialogComponent.java index 6d55f54..4e07274 100644 --- a/src/main/java/ru/otus/mobile/component/AlertDialogComponent.java +++ b/src/main/java/ru/otus/mobile/component/AlertDialogComponent.java @@ -1,25 +1,27 @@ package ru.otus.mobile.component; +import com.codeborne.selenide.SelenideElement; 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 { + private final SelenideElement positiveButton; + @Inject - public AlertDialogComponent(MobileConfig config) { - super(config); + public AlertDialogComponent() { + this($(AppiumBy.id("android:id/content"))); + } + + public AlertDialogComponent(SelenideElement root) { + super(root); + this.positiveButton = byIdInRoot("android:id/button1"); } public void acceptIfVisible() { - if (exists("android:id/button1")) { - tap("android:id/button1"); - return; - } - if (byText("OK").exists()) { - byText("OK").click(); - return; - } - if (byText("ОК").exists()) { - byText("ОК").click(); + if (positiveButton.exists()) { + positiveButton.click(); } } } diff --git a/src/main/java/ru/otus/mobile/component/BaseMobileComponent.java b/src/main/java/ru/otus/mobile/component/BaseMobileComponent.java index 4b108cf..18633d9 100644 --- a/src/main/java/ru/otus/mobile/component/BaseMobileComponent.java +++ b/src/main/java/ru/otus/mobile/component/BaseMobileComponent.java @@ -1,10 +1,22 @@ 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; public abstract class BaseMobileComponent extends AbsPageObject { - protected BaseMobileComponent(MobileConfig config) { - super(config); + protected final SelenideElement root; + + 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))); } } diff --git a/src/main/java/ru/otus/mobile/component/BottomNavigationComponent.java b/src/main/java/ru/otus/mobile/component/BottomNavigationComponent.java index de0e9a5..fc60018 100644 --- a/src/main/java/ru/otus/mobile/component/BottomNavigationComponent.java +++ b/src/main/java/ru/otus/mobile/component/BottomNavigationComponent.java @@ -1,19 +1,31 @@ package ru.otus.mobile.component; +import com.codeborne.selenide.SelenideElement; 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 { + private final SelenideElement mineMenu; + private final SelenideElement usersMenu; + @Inject - public BottomNavigationComponent(MobileConfig config) { - super(config); + public BottomNavigationComponent() { + 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() { - tap("mine_menu"); + mineMenu.click(); } public void openUsers() { - tap("users_menu"); + usersMenu.click(); } } diff --git a/src/main/java/ru/otus/mobile/component/GiftFormComponent.java b/src/main/java/ru/otus/mobile/component/GiftFormComponent.java index 411779d..7976509 100644 --- a/src/main/java/ru/otus/mobile/component/GiftFormComponent.java +++ b/src/main/java/ru/otus/mobile/component/GiftFormComponent.java @@ -1,17 +1,35 @@ package ru.otus.mobile.component; +import com.codeborne.selenide.SelenideElement; 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 { + private final SelenideElement nameInput; + private final SelenideElement priceInput; + private final SelenideElement saveButton; + @Inject - public GiftFormComponent(MobileConfig config) { - super(config); + public GiftFormComponent() { + 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) { - type("name_input", name); - type("price_input", price); - tap("save_button"); + nameInput.click(); + nameInput.clear(); + nameInput.sendKeys(name); + priceInput.click(); + priceInput.clear(); + priceInput.sendKeys(price); + saveButton.click(); } } diff --git a/src/main/java/ru/otus/mobile/component/GiftItemComponent.java b/src/main/java/ru/otus/mobile/component/GiftItemComponent.java new file mode 100644 index 0000000..3a217a1 --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/GiftItemComponent.java @@ -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(); + } +} diff --git a/src/main/java/ru/otus/mobile/component/GiftsContentComponent.java b/src/main/java/ru/otus/mobile/component/GiftsContentComponent.java new file mode 100644 index 0000000..1ada6d5 --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/GiftsContentComponent.java @@ -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."); + } +} diff --git a/src/main/java/ru/otus/mobile/component/TopBarComponent.java b/src/main/java/ru/otus/mobile/component/TopBarComponent.java new file mode 100644 index 0000000..ad2abda --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/TopBarComponent.java @@ -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."); + } +} diff --git a/src/main/java/ru/otus/mobile/component/UserItemComponent.java b/src/main/java/ru/otus/mobile/component/UserItemComponent.java new file mode 100644 index 0000000..50d9e8d --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/UserItemComponent.java @@ -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(); + } +} diff --git a/src/main/java/ru/otus/mobile/component/UsersContentComponent.java b/src/main/java/ru/otus/mobile/component/UsersContentComponent.java new file mode 100644 index 0000000..75f79e7 --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/UsersContentComponent.java @@ -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."); + } +} diff --git a/src/main/java/ru/otus/mobile/component/UsersFilterComponent.java b/src/main/java/ru/otus/mobile/component/UsersFilterComponent.java new file mode 100644 index 0000000..5117f3c --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/UsersFilterComponent.java @@ -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"); + } +} diff --git a/src/main/java/ru/otus/mobile/component/WishlistFormComponent.java b/src/main/java/ru/otus/mobile/component/WishlistFormComponent.java index 62624f6..8f0f7cb 100644 --- a/src/main/java/ru/otus/mobile/component/WishlistFormComponent.java +++ b/src/main/java/ru/otus/mobile/component/WishlistFormComponent.java @@ -1,16 +1,30 @@ package ru.otus.mobile.component; +import com.codeborne.selenide.SelenideElement; 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 { + private final SelenideElement titleInput; + private final SelenideElement saveButton; + @Inject - public WishlistFormComponent(MobileConfig config) { - super(config); + public WishlistFormComponent() { + 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) { - type("title_input", title); - tap("save_button"); + titleInput.click(); + titleInput.clear(); + titleInput.sendKeys(title); + saveButton.click(); } } diff --git a/src/main/java/ru/otus/mobile/component/WishlistItemComponent.java b/src/main/java/ru/otus/mobile/component/WishlistItemComponent.java new file mode 100644 index 0000000..f6f5412 --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/WishlistItemComponent.java @@ -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(); + } +} diff --git a/src/main/java/ru/otus/mobile/component/WishlistsContentComponent.java b/src/main/java/ru/otus/mobile/component/WishlistsContentComponent.java new file mode 100644 index 0000000..e2a8037 --- /dev/null +++ b/src/main/java/ru/otus/mobile/component/WishlistsContentComponent.java @@ -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."); + } +} diff --git a/src/main/java/ru/otus/mobile/page/AbsBasePage.java b/src/main/java/ru/otus/mobile/page/AbsBasePage.java index 03a1def..62801be 100644 --- a/src/main/java/ru/otus/mobile/page/AbsBasePage.java +++ b/src/main/java/ru/otus/mobile/page/AbsBasePage.java @@ -1,19 +1,11 @@ package ru.otus.mobile.page; import ru.otus.mobile.component.BottomNavigationComponent; -import ru.otus.mobile.config.MobileConfig; public abstract class AbsBasePage extends AbsPageObject { protected final BottomNavigationComponent bottomNavigation; - private final MobileConfig config; - protected AbsBasePage(MobileConfig config, BottomNavigationComponent bottomNavigation) { - super(config); - this.config = config; + protected AbsBasePage(BottomNavigationComponent bottomNavigation) { this.bottomNavigation = bottomNavigation; } - - protected MobileConfig config() { - return config; - } } diff --git a/src/main/java/ru/otus/mobile/page/AbsPageObject.java b/src/main/java/ru/otus/mobile/page/AbsPageObject.java index 013f88d..f12bebd 100644 --- a/src/main/java/ru/otus/mobile/page/AbsPageObject.java +++ b/src/main/java/ru/otus/mobile/page/AbsPageObject.java @@ -1,45 +1,24 @@ package ru.otus.mobile.page; -import com.codeborne.selenide.CollectionCondition; import com.codeborne.selenide.Condition; import com.codeborne.selenide.SelenideElement; import com.codeborne.selenide.WebDriverRunner; import com.codeborne.selenide.appium.SelenideAppiumCollection; 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.$; public abstract class AbsPageObject { - private final MobileConfig config; - - protected AbsPageObject(MobileConfig config) { - this.config = config; - } + private static final String APP_PACKAGE_PROPERTY = "app.package"; + private static final String DEFAULT_APP_PACKAGE = "ru.otus.wishlist"; protected SelenideElement byId(String id) { - return $(AppiumBy.id(fullId(id))); + return $(AppiumBy.id(fullIdValue(id))); } protected SelenideAppiumCollection allById(String id) { - return $$(AppiumBy.id(fullId(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)); + return $$(AppiumBy.id(fullIdValue(id))); } protected void type(String id, String value) { @@ -57,47 +36,15 @@ public abstract class AbsPageObject { 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() { WebDriverRunner.getWebDriver().navigate().back(); } - protected String xpathLiteral(String value) { - 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) { + protected static String fullIdValue(String id) { if (id.contains(":")) { return id; } - return config.appPackage() + ":id/" + id; - } - - private String escape(String value) { - return value.replace("\"", "\\\""); + String appPackage = System.getProperty(APP_PACKAGE_PROPERTY, DEFAULT_APP_PACKAGE); + return appPackage + ":id/" + id; } } diff --git a/src/main/java/ru/otus/mobile/page/GiftsPage.java b/src/main/java/ru/otus/mobile/page/GiftsPage.java index d4f56ec..ac09161 100644 --- a/src/main/java/ru/otus/mobile/page/GiftsPage.java +++ b/src/main/java/ru/otus/mobile/page/GiftsPage.java @@ -3,34 +3,39 @@ package ru.otus.mobile.page; import com.codeborne.selenide.Condition; import com.google.inject.Inject; import ru.otus.mobile.component.BottomNavigationComponent; +import ru.otus.mobile.component.GiftsContentComponent; import ru.otus.mobile.component.GiftFormComponent; -import ru.otus.mobile.config.MobileConfig; public final class GiftsPage extends AbsBasePage { private final GiftFormComponent form; + private final com.codeborne.selenide.SelenideElement addButton = byId("add_button"); @Inject - public GiftsPage(MobileConfig config, BottomNavigationComponent bottomNavigation, GiftFormComponent form) { - super(config, bottomNavigation); + public GiftsPage(BottomNavigationComponent bottomNavigation, GiftFormComponent form) { + super(bottomNavigation); this.form = form; } public void createGift(String name) { - tap("add_button"); + addButton.click(); form.save(name, "100"); } public void editGift(String oldName, String newName) { openGift(oldName); - tap("edit_button"); + content().byTitle(oldName).edit(); form.save(newName, "100"); } public void shouldSeeGift(String name) { - scrollToText(name).shouldBe(Condition.visible); + content().byTitle(name).shouldHaveTitle(name); } private void openGift(String name) { - scrollToText(name).click(); + content().byTitle(name).open(); + } + + private GiftsContentComponent content() { + return new GiftsContentComponent(byId("gifts_content")); } } diff --git a/src/main/java/ru/otus/mobile/page/LoginPage.java b/src/main/java/ru/otus/mobile/page/LoginPage.java index 433350c..6377ff3 100644 --- a/src/main/java/ru/otus/mobile/page/LoginPage.java +++ b/src/main/java/ru/otus/mobile/page/LoginPage.java @@ -2,15 +2,13 @@ package ru.otus.mobile.page; import com.google.inject.Inject; import ru.otus.mobile.component.AlertDialogComponent; -import ru.otus.mobile.config.MobileConfig; import ru.otus.mobile.config.TestAccount; public final class LoginPage extends AbsPageObject { private final AlertDialogComponent alertDialog; @Inject - public LoginPage(MobileConfig config, AlertDialogComponent alertDialog) { - super(config); + public LoginPage(AlertDialogComponent alertDialog) { this.alertDialog = alertDialog; } diff --git a/src/main/java/ru/otus/mobile/page/UsersPage.java b/src/main/java/ru/otus/mobile/page/UsersPage.java index 6fa6ce4..9680a06 100644 --- a/src/main/java/ru/otus/mobile/page/UsersPage.java +++ b/src/main/java/ru/otus/mobile/page/UsersPage.java @@ -1,69 +1,55 @@ package ru.otus.mobile.page; import com.codeborne.selenide.Condition; -import com.codeborne.selenide.CollectionCondition; -import com.codeborne.selenide.SelenideElement; import com.google.inject.Inject; 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; public final class UsersPage extends AbsBasePage { + private final TopBarComponent topBar; + @Inject - public UsersPage(MobileConfig config, BottomNavigationComponent bottomNavigation) { - super(config, bottomNavigation); + public UsersPage(BottomNavigationComponent bottomNavigation) { + super(bottomNavigation); + this.topBar = new TopBarComponent(byId("android:id/content")); } public void open() { bottomNavigation.openUsers(); - byId("users_content").shouldBe(Condition.visible); + usersContentRoot().shouldBe(Condition.visible, Duration.ofSeconds(15)); } public void filterByUsername(String username) { - openFilter(); - SelenideElement input = filterInput().shouldBe(Condition.visible, Duration.ofSeconds(15)); - input.clear(); - input.setValue(username); - applyFilter(); - byTextContains(username).shouldBe(Condition.visible, Duration.ofSeconds(15)); + topBar.openUsersFilter(); + byId("users_filter_bottom_sheet").shouldBe(Condition.visible, Duration.ofSeconds(15)); + new UsersFilterComponent(byId("users_filter_bottom_sheet")).applyByUsername(username); + byId("user_item").shouldBe(Condition.visible, Duration.ofSeconds(15)); } public void openUser(String username) { - String appPackage = config().appPackage(); - 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)); + usersContent().byUsername(username).open(); } public void openFirstWishlist() { - allById("wishlist_item") - .shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15)) - .first() - .shouldBe(Condition.visible, Duration.ofSeconds(15)) - .click(); + wishlistsContent().get(0).open(); } public void openWishlist(String name) { - scrollToText(name).shouldBe(Condition.visible, Duration.ofSeconds(15)).click(); + wishlistsContent().byTitle(name).open(); } public void openFirstGift() { - allById("gift_item") - .shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15)) - .first() - .shouldBe(Condition.visible, Duration.ofSeconds(15)) - .click(); + new GiftsContentComponent(byId("gifts_content")).get(0).open(); } 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() { @@ -71,87 +57,29 @@ public final class UsersPage extends AbsBasePage { } public void toggleReservation() { - tap("reserved"); + byId("reserved").shouldBe(Condition.visible).click(); } - private void openFilter() { - if (byId("users_filter").exists()) { - 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 UsersContentComponent usersContent() { + return new UsersContentComponent(usersContentRoot()); } - private SelenideElement filterInput() { - if (byId("filter_input").exists()) { - 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 WishlistsContentComponent wishlistsContent() { + return new WishlistsContentComponent(wishlistsContentRoot()); } - private void applyFilter() { - if (byId("apply_button").exists()) { - tap("apply_button"); - return; + private com.codeborne.selenide.SelenideElement usersContentRoot() { + if (byId("users_content").exists()) { + return byId("users_content"); } - if (byId("apply").exists()) { - tap("apply"); - return; - } - if (byText("ПРИМЕНИТЬ").exists()) { - byText("ПРИМЕНИТЬ").click(); - return; - } - if (byText("APPLY").exists()) { - byText("APPLY").click(); - return; - } - throw new IllegalStateException("Apply filter button was not found on Users filter screen."); + return byId("users"); } + + private com.codeborne.selenide.SelenideElement wishlistsContentRoot() { + if (byId("wishlists_content").exists()) { + return byId("wishlists_content"); + } + return byId("wishlists"); + } + } diff --git a/src/main/java/ru/otus/mobile/page/WishlistsPage.java b/src/main/java/ru/otus/mobile/page/WishlistsPage.java index 1d38b8e..ee7afec 100644 --- a/src/main/java/ru/otus/mobile/page/WishlistsPage.java +++ b/src/main/java/ru/otus/mobile/page/WishlistsPage.java @@ -1,72 +1,67 @@ package ru.otus.mobile.page; import com.codeborne.selenide.Condition; -import com.codeborne.selenide.CollectionCondition; import com.google.inject.Inject; import ru.otus.mobile.component.BottomNavigationComponent; +import ru.otus.mobile.component.WishlistsContentComponent; import ru.otus.mobile.component.WishlistFormComponent; -import ru.otus.mobile.config.MobileConfig; - -import java.time.Duration; public final class WishlistsPage extends AbsBasePage { private final WishlistFormComponent form; + private final com.codeborne.selenide.SelenideElement addButton = byId("add_button"); @Inject - public WishlistsPage(MobileConfig config, BottomNavigationComponent bottomNavigation, WishlistFormComponent form) { - super(config, bottomNavigation); + public WishlistsPage(BottomNavigationComponent bottomNavigation, WishlistFormComponent form) { + super(bottomNavigation); this.form = form; } public void open() { bottomNavigation.openWishlists(); - byId("wishlists_content").shouldBe(Condition.visible); + contentRoot().shouldBe(Condition.visible); } public void createWishlist(String name) { - tap("add_button"); + addButton.click(); form.save(name); } public void editWishlist(String oldName, String newName) { ensureWishlistsList(); - editButtonFor(oldName).shouldBe(Condition.visible).click(); + content().byTitle(oldName).edit(); form.save(newName); } public void shouldSeeWishlist(String name) { - scrollToText(name).shouldBe(Condition.visible); + content().byTitle(name).shouldHaveTitle(name); } public void openWishlist(String name) { - scrollToText(name).click(); + content().byTitle(name).open(); } public void openFirstWishlist() { - allById("wishlist_item") - .shouldHave(CollectionCondition.sizeGreaterThan(0), Duration.ofSeconds(15)) - .first() - .shouldBe(Condition.visible, Duration.ofSeconds(15)) - .click(); + content().first().open(); } private void ensureWishlistsList() { - if (exists("wishlist_item") || exists("wishlists")) { + if (exists("wishlist_item") || contentRoot().exists()) { return; } if (exists("gifts_content") || exists("gift_item") || exists("add_button")) { back(); } - byId("wishlists_content").shouldBe(Condition.visible); + contentRoot().shouldBe(Condition.visible); } - private com.codeborne.selenide.SelenideElement editButtonFor(String name) { - String full = config().appPackage(); - 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'" - + " and contains(@text," + xpathLiteral(name) + ")]]" - + "//android.widget.Button[@resource-id='" + full + ":id/edit_button']"; - return byXpath(xpath); + private WishlistsContentComponent content() { + return new WishlistsContentComponent(contentRoot()); + } + + private com.codeborne.selenide.SelenideElement contentRoot() { + if (byId("wishlists_content").exists()) { + return byId("wishlists_content"); + } + return byId("wishlists"); } }