From e2bd5080368a03b5c0f807b641e738a568888058 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 15 May 2026 20:12:57 +0300 Subject: [PATCH 1/2] Issue-114 Web UI tests added --- java-reporter-testng-selenide/pom.xml | 60 ++++++ .../src/main/java/io/testomat/data/Users.java | 28 +++ .../main/java/io/testomat/pages/CartPage.java | 24 +++ .../java/io/testomat/pages/InventoryPage.java | 102 ++++++++++ .../java/io/testomat/pages/LoginPage.java | 76 ++++++++ .../java/io/testomat/pages/ProductPage.java | 93 +++++++++ .../java/io/testomat/steps/LoginSteps.java | 27 +++ .../src/main/resources/testomatio.properties | 11 ++ .../src/test/java/base/BaseTest.java | 28 +++ .../src/test/java/tests/InventoryTest.java | 181 ++++++++++++++++++ .../src/test/java/tests/LoginTest.java | 133 +++++++++++++ .../test/java/tests/ProductDetailsTest.java | 160 ++++++++++++++++ java-reporter-testng-selenide/testng.xml | 11 ++ 13 files changed, 934 insertions(+) create mode 100644 java-reporter-testng-selenide/pom.xml create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/data/Users.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java create mode 100644 java-reporter-testng-selenide/src/main/resources/testomatio.properties create mode 100644 java-reporter-testng-selenide/src/test/java/base/BaseTest.java create mode 100644 java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java create mode 100644 java-reporter-testng-selenide/src/test/java/tests/LoginTest.java create mode 100644 java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java create mode 100644 java-reporter-testng-selenide/testng.xml diff --git a/java-reporter-testng-selenide/pom.xml b/java-reporter-testng-selenide/pom.xml new file mode 100644 index 0000000..840e385 --- /dev/null +++ b/java-reporter-testng-selenide/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + io.testomat + java-reporter-testng-selenide + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + 7.16.1 + 7.11.0 + 2.29.1 + + + + + com.codeborne + selenide + ${selenide.version} + + + org.testng + testng + ${testng.version} + test + + + io.testomat + java-reporter-testng + 0.12.0 + + + org.assertj + assertj-core + 3.27.3 + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.3 + + + testng.xml + + + + + + + \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/data/Users.java b/java-reporter-testng-selenide/src/main/java/io/testomat/data/Users.java new file mode 100644 index 0000000..8db86f7 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/data/Users.java @@ -0,0 +1,28 @@ +package io.testomat.data; + +public final class Users { + + public static final String PASSWORD = + "secret_sauce"; + + public static final String STANDARD_USER = + "standard_user"; + + public static final String LOCKED_USER = + "locked_out_user"; + + public static final String PROBLEM_USER = + "problem_user"; + + public static final String PERFORMANCE_USER = + "performance_glitch_user"; + + public static final String ERROR_USER = + "error_user"; + + public static final String VISUAL_USER = + "visual_user"; + + private Users() { + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java new file mode 100644 index 0000000..b879a78 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java @@ -0,0 +1,24 @@ +package io.testomat.pages; + +import com.codeborne.selenide.ElementsCollection; + +import static com.codeborne.selenide.CollectionCondition.sizeGreaterThan; +import static com.codeborne.selenide.Selenide.$$; + +public class CartPage { + + private final ElementsCollection cartItems = + $$(".cart_item"); + + public CartPage verifyItemsExist() { + + cartItems.shouldHave(sizeGreaterThan(0)); + + return this; + } + + public int getItemsCount() { + + return cartItems.size(); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java new file mode 100644 index 0000000..ef15228 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java @@ -0,0 +1,102 @@ +package io.testomat.pages; + +import com.codeborne.selenide.ElementsCollection; +import com.codeborne.selenide.SelenideElement; + +import java.util.List; + +import static com.codeborne.selenide.CollectionCondition.sizeGreaterThan; +import static com.codeborne.selenide.Condition.*; +import static com.codeborne.selenide.Selenide.*; + +public class InventoryPage { + + private final SelenideElement title = + $(".title"); + + private final ElementsCollection inventoryItems = + $$(".inventory_item"); + + private final ElementsCollection itemNames = + $$(".inventory_item_name"); + + private final ElementsCollection itemPrices = + $$(".inventory_item_price"); + + private final SelenideElement cartButton = + $(".shopping_cart_link"); + + private final SelenideElement sortDropdown = + $(".product_sort_container"); + + private final ElementsCollection addToCartButtons = + $$("button[id^='add-to-cart']"); + + private final ElementsCollection removeButtons = + $$("button[id^='remove']"); + + private final SelenideElement burgerMenu = + $("#react-burger-menu-btn"); + + private final SelenideElement logoutLink = + $("#logout_sidebar_link"); + + public InventoryPage verifyPageLoaded() { + title.shouldHave(text("Products")); + inventoryItems.shouldHave(sizeGreaterThan(0)); + + return this; + } + + public int getItemsCount() { + + return inventoryItems.size(); + } + + public List getItemNames() { + + return itemNames.texts(); + } + + public List getItemPrices() { + + return itemPrices.texts(); + } + + public InventoryPage sortBy(String value) { + + sortDropdown.selectOptionByValue(value); + + return this; + } + + public InventoryPage addFirstItemToCart() { + + addToCartButtons.first().click(); + + return this; + } + + public InventoryPage removeFirstItemFromCart() { + + removeButtons.first().click(); + + return this; + } + + public CartPage openCart() { + + cartButton.click(); + + return new CartPage(); + } + + public LoginPage logout() { + + burgerMenu.click(); + + logoutLink.shouldBe(visible).click(); + + return new LoginPage(); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java new file mode 100644 index 0000000..dd17802 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java @@ -0,0 +1,76 @@ +package io.testomat.pages; + +import com.codeborne.selenide.SelenideElement; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.open; + +public class LoginPage { + + private final SelenideElement rootContainer = $("[data-test='login-container']"); + private final SelenideElement usernameInput = $("#user-name"); + private final SelenideElement passwordInput = $("#password"); + private final SelenideElement loginButton = $("#login-button"); + + private final SelenideElement errorMessage = $("h3[data-test='error']"); + + public LoginPage openPage() { + open("https://www.saucedemo.com/"); + + return this; + } + + public LoginPage verifyErrorMessage(String text) { + errorMessage.shouldHave(text(text)); + + return this; + } + + public LoginPage enterUsername(String username) { + usernameInput + .shouldBe(visible) + .sendKeys(username); + + return this; + } + + public LoginPage enterPassword(String password) { + passwordInput + .shouldBe(visible) + .sendKeys(password); + + return this; + } + + public InventoryPage clickLogin() { + loginButton + .shouldBe(visible) + .click(); + + return new InventoryPage(); + } + + public LoginPage login() { + loginButton + .shouldBe(visible) + .click(); + + return this; + } + + public LoginPage clickLoginExpectingFailure() { + loginButton + .shouldBe(visible) + .click(); + + return this; + } + + public LoginPage verifyLoginButtonVisible() { + loginButton.shouldBe(visible); + + return this; + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java new file mode 100644 index 0000000..25471ad --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java @@ -0,0 +1,93 @@ +package io.testomat.pages; + +import com.codeborne.selenide.SelenideElement; + +import static com.codeborne.selenide.Condition.*; +import static com.codeborne.selenide.Selenide.$; + +public class ProductPage { + + private final SelenideElement productName = + $(".inventory_details_name"); + + private final SelenideElement productDescription = + $(".inventory_details_desc"); + + private final SelenideElement productPrice = + $(".inventory_details_price"); + + private final SelenideElement productImage = + $(".inventory_details_img img"); + + private final SelenideElement addToCartButton = + $("button[id^='add-to-cart']"); + + private final SelenideElement removeButton = + $("button[id^='remove']"); + + private final SelenideElement backButton = + $("#back-to-products"); + + private final SelenideElement cartBadge = + $(".shopping_cart_badge"); + + public ProductPage verifyPageLoaded() { + + productName.shouldBe(visible); + + productDescription.shouldBe(visible); + + productPrice.shouldBe(visible); + + productImage.shouldBe(visible); + + return this; + } + + public String getProductName() { + + return productName.getText(); + } + + public String getProductDescription() { + + return productDescription.getText(); + } + + public String getProductPrice() { + + return productPrice.getText(); + } + + public ProductPage addToCart() { + + addToCartButton + .shouldBe(enabled) + .click(); + + return this; + } + + public ProductPage removeFromCart() { + + removeButton + .shouldBe(enabled) + .click(); + + return this; + } + + public ProductPage verifyCartBadge(String count) { + + cartBadge.shouldHave(text(count)); + + return this; + } + + public InventoryPage clickBackButton() { + + backButton.click(); + + return new InventoryPage(); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java new file mode 100644 index 0000000..8f1f533 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java @@ -0,0 +1,27 @@ +package io.testomat.steps; + +import io.testomat.data.Users; +import io.testomat.pages.InventoryPage; +import io.testomat.pages.LoginPage; + +public class LoginSteps { + + public static InventoryPage loginAsStandardUser() { + return new LoginPage() + .openPage() + .enterUsername("standard_user") + .enterPassword("secret_sauce") + .clickLogin() + .verifyPageLoaded(); + } + + public static InventoryPage loginAs(String username) { + + return new LoginPage() + .openPage() + .enterUsername(username) + .enterPassword(Users.PASSWORD) + .clickLogin() + .verifyPageLoaded(); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/resources/testomatio.properties b/java-reporter-testng-selenide/src/main/resources/testomatio.properties new file mode 100644 index 0000000..23fd259 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/resources/testomatio.properties @@ -0,0 +1,11 @@ +#Change to https://beta.testomat.io/ if you use it +testomatio.url=https://app.testomat.io/ + +#define the run title, or it will be default_run_title +testomatio.run.title=testng-example-run + +#Particular project api key, starts with "tstmt_" +testomatio.api.key= + +#enables/disables the reporting (remove value to disable) +testomatio.listening=true \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/test/java/base/BaseTest.java b/java-reporter-testng-selenide/src/test/java/base/BaseTest.java new file mode 100644 index 0000000..a8f6d10 --- /dev/null +++ b/java-reporter-testng-selenide/src/test/java/base/BaseTest.java @@ -0,0 +1,28 @@ +package base; + +import com.codeborne.selenide.Configuration; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; + +import static com.codeborne.selenide.Selenide.closeWebDriver; + +public class BaseTest { + + @BeforeMethod + public void setup() { + Configuration.browser = "chrome"; + Configuration.browserSize = "1920x1080"; + Configuration.timeout = 10000; + Configuration.pageLoadTimeout = 60000; + Configuration.headless = false; + Configuration.screenshots = true; + Configuration.savePageSource = true; + Configuration.reopenBrowserOnFail = true; + Configuration.fastSetValue = true; + } + + @AfterMethod(alwaysRun = true) + public void tearDown() { + closeWebDriver(); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java b/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java new file mode 100644 index 0000000..43ae1c2 --- /dev/null +++ b/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java @@ -0,0 +1,181 @@ +package tests; + +import static com.codeborne.selenide.Condition.empty; +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; +import static com.codeborne.selenide.Selenide.open; +import static com.codeborne.selenide.Selenide.refresh; +import static com.codeborne.selenide.Selenide.webdriver; +import static com.codeborne.selenide.WebDriverConditions.url; + +import base.BaseTest; +import java.util.Comparator; +import java.util.List; +import org.testng.Assert; +import org.testng.annotations.Test; +import io.testomat.pages.CartPage; +import io.testomat.pages.InventoryPage; +import io.testomat.pages.LoginPage; +import io.testomat.steps.LoginSteps; + +public class InventoryTest extends BaseTest { + + @Test + public void inventoryShouldBeLoaded() { + InventoryPage page = LoginSteps.loginAsStandardUser(); + Assert.assertTrue(page.getItemsCount() > 0); + } + + @Test + public void inventoryTitleShouldBeVisible() { + LoginSteps.loginAsStandardUser() + .verifyPageLoaded(); + } + + @Test + public void itemShouldBeAddedToCart() { + CartPage cartPage = + LoginSteps.loginAsStandardUser() + .addFirstItemToCart() + .openCart() + .verifyItemsExist(); + + Assert.assertEquals(cartPage.getItemsCount(), 1); + } + + @Test + public void itemShouldBeRemovedFromCart() { + InventoryPage page = + LoginSteps.loginAsStandardUser() + .addFirstItemToCart() + .removeFirstItemFromCart(); + + CartPage cartPage = page.openCart(); + + Assert.assertEquals(cartPage.getItemsCount(), 0); + } + + @Test + public void userShouldLogoutSuccessfully() { + LoginPage loginPage = + LoginSteps.loginAsStandardUser() + .logout(); + + loginPage.verifyLoginButtonVisible(); + } + + @Test + public void productsShouldBeSortedByNameAZ() { + InventoryPage page = + LoginSteps.loginAsStandardUser() + .sortBy("az"); + + List names = page.getItemNames(); + + List sorted = + names.stream() + .sorted() + .toList(); + + Assert.assertEquals(names, sorted); + } + + @Test + public void productsShouldBeSortedByNameZA() { + InventoryPage page = + LoginSteps.loginAsStandardUser() + .sortBy("za"); + + List names = page.getItemNames(); + + List sorted = + names.stream() + .sorted(Comparator.reverseOrder()) + .toList(); + + Assert.assertEquals(names, sorted); + } + + @Test + public void productsShouldBeSortedByPriceLowToHigh() { + InventoryPage page = + LoginSteps.loginAsStandardUser() + .sortBy("lohi"); + + List prices = + page.getItemPrices() + .stream() + .map(p -> Double.parseDouble( + p.replace("$", "") + )) + .toList(); + + List sorted = + prices.stream() + .sorted() + .toList(); + + Assert.assertEquals(prices, sorted); + } + + @Test + public void userShouldNotAccessInventoryWithoutLogin() { + open("https://www.saucedemo.com/inventory.html"); + webdriver().shouldHave(url("https://www.saucedemo.com/")); + } + + @Test + public void lockedUserShouldNotLogin() { + new LoginPage() + .openPage() + .enterUsername("locked_out_user") + .enterPassword("secret_sauce") + .clickLoginExpectingFailure() + .verifyErrorMessage( + "Sorry, this user has been locked out." + ); + } + + @Test + public void cartShouldBeEmptyInitially() { + CartPage cartPage = + LoginSteps.loginAsStandardUser() + .openCart(); + + Assert.assertEquals(cartPage.getItemsCount(), 0); + } + + @Test + public void allProductImagesShouldBeVisible() { + $$(".inventory_item_img img").forEach(img -> img.shouldBe(visible)); + } + + @Test + public void addToCartButtonsShouldBeVisible() { + LoginSteps.loginAsStandardUser(); + $$("button[id^='add-to-cart']") + .forEach(btn -> + btn.shouldBe(visible)); + } + + @Test + public void allPricesShouldBeVisible() { + LoginSteps.loginAsStandardUser(); + $$(".inventory_item_price") + .forEach(price -> + price.shouldNotBe(empty) + ); + } + + @Test + public void cartShouldPersistAfterRefresh() { + LoginSteps.loginAsStandardUser() + .addFirstItemToCart(); + refresh(); + + $(".shopping_cart_badge") + .shouldHave(text("1")); + } +} diff --git a/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java b/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java new file mode 100644 index 0000000..660cfca --- /dev/null +++ b/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java @@ -0,0 +1,133 @@ +package tests; + +import static com.codeborne.selenide.Condition.attributeMatching; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; +import static com.codeborne.selenide.Selenide.open; + +import base.BaseTest; +import io.testomat.data.Users; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import io.testomat.pages.LoginPage; +import io.testomat.steps.LoginSteps; + +public class LoginTest extends BaseTest { + + @DataProvider(name = "validUsers") + public Object[][] validUsers() { + + return new Object[][]{ + {Users.STANDARD_USER}, + {Users.PROBLEM_USER}, + {Users.PERFORMANCE_USER}, + {Users.ERROR_USER}, + {Users.VISUAL_USER} + }; + } + + @Test(dataProvider = "validUsers") + public void validUsersShouldLogin(String username) { + + new LoginPage() + .openPage() + .enterUsername(username) + .enterPassword(Users.PASSWORD) + .clickLogin() + .verifyPageLoaded(); + } + + @Test + public void lockedUserShouldSeeError() { + new LoginPage() + .openPage() + .enterUsername(Users.LOCKED_USER) + .enterPassword(Users.PASSWORD) + .clickLoginExpectingFailure() + .verifyErrorMessage( + "Sorry, this user has been locked out." + ); + } + + @Test + public void performanceUserShouldLoginSuccessfully() { + + long start = System.currentTimeMillis(); + + new LoginPage() + .openPage() + .enterUsername(Users.PERFORMANCE_USER) + .enterPassword(Users.PASSWORD) + .clickLogin() + .verifyPageLoaded(); + + long elapsed = + System.currentTimeMillis() - start; + + Assert.assertTrue( + elapsed > 2000 + ); + } + + @Test + public void performanceUserInventoryShouldLoad() { + new LoginPage() + .openPage() + .enterUsername(Users.PERFORMANCE_USER) + .enterPassword(Users.PASSWORD) + .clickLogin() + .verifyPageLoaded(); + } + + @Test + public void problemUserShouldHaveBrokenImages() { + new LoginPage() + .openPage() + .enterUsername(Users.PROBLEM_USER) + .enterPassword(Users.PASSWORD) + .clickLogin(); + + $$("img") + .forEach(img -> + img.shouldHave(attributeMatching( + "src", + ".*sl-404.*" + )) + ); + } + + @Test + public void problemUserCanStillAddItemsToCart() { + + LoginSteps.loginAs(Users.PROBLEM_USER) + .addFirstItemToCart() + .openCart() + .verifyItemsExist(); + } + + @Test + public void errorUserShouldNavigateToProductPage() { + LoginSteps.loginAs(Users.ERROR_USER); + open("https://www.saucedemo.com/inventory-item.html?id=4"); + $(".inventory_details_name").shouldBe(visible); + } + + @Test + public void visualUserShouldOpenInventoryPage() { + LoginSteps.loginAs(Users.VISUAL_USER).verifyPageLoaded(); + } + + @Test + public void visualUserShouldSeeMainElements() { + LoginSteps.loginAs(Users.VISUAL_USER); + + $(".inventory_list").shouldBe(visible); + $(".shopping_cart_link").shouldBe(visible); + $(".product_sort_container").shouldBe(visible); + } + + + +} diff --git a/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java b/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java new file mode 100644 index 0000000..27108f5 --- /dev/null +++ b/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java @@ -0,0 +1,160 @@ +package tests; + +import base.BaseTest; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import io.testomat.pages.LoginPage; +import io.testomat.pages.ProductPage; + +import static com.codeborne.selenide.Condition.attributeMatching; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.open; +import static com.codeborne.selenide.Selenide.refresh; +import static com.codeborne.selenide.Selenide.webdriver; +import static com.codeborne.selenide.WebDriverConditions.url; + +public class ProductDetailsTest extends BaseTest { + + @BeforeMethod + public void login() { + new LoginPage() + .openPage() + .enterUsername("standard_user") + .enterPassword("secret_sauce") + .clickLogin(); + } + + private ProductPage openProductPage() { + open("https://www.saucedemo.com/inventory-item.html?id=4"); + + return new ProductPage() + .verifyPageLoaded(); + } + + @Test + public void productPageShouldBeOpened() { + openProductPage(); + } + + @Test + public void productNameShouldBeVisible() { + ProductPage page = + openProductPage(); + + Assert.assertFalse( + page.getProductName().isBlank() + ); + } + + @Test + public void productDescriptionShouldBeVisible() { + ProductPage page = + openProductPage(); + + Assert.assertFalse( + page.getProductDescription().isBlank() + ); + } + + @Test + public void productPriceShouldBeVisible() { + ProductPage page = + openProductPage(); + + Assert.assertTrue( + page.getProductPrice().contains("$") + ); + } + + @Test + public void productImageShouldBeVisible() { + openProductPage(); + } + + @Test + public void itemShouldBeAddedToCart() { + openProductPage() + .addToCart() + .verifyCartBadge("1"); + } + + @Test + public void itemShouldBeRemovedFromCart() { + openProductPage() + .addToCart() + .removeFromCart(); + } + + @Test + public void backButtonShouldReturnToInventory() { + openProductPage() + .clickBackButton() + .verifyPageLoaded(); + } + + @Test + public void cartBadgeShouldIncreaseAfterAdd() { + openProductPage() + .addToCart() + .verifyCartBadge("1"); + } + + @Test + public void addToCartButtonShouldChangeToRemove() { + openProductPage() + .addToCart(); + } + + @Test + public void pageShouldBeRefreshSafe() { + ProductPage page = + openProductPage(); + + page.addToCart(); + refresh(); + + page.verifyCartBadge("1"); + } + + @Test + public void userShouldNotAccessProductPageWithoutLogin() { + open("https://www.saucedemo.com/inventory-item.html?id=4"); + webdriver().shouldHave( + url("https://www.saucedemo.com/") + ); + } + + @Test + public void invalidProductIdShouldNotCrashApplication() { + open("https://www.saucedemo.com/inventory-item.html?id=999"); + } + + @Test + public void correctProductShouldBeDisplayed() { + ProductPage page = + openProductPage(); + + Assert.assertEquals( + page.getProductName(), + "Sauce Labs Backpack" + ); + } + + @Test + public void imageShouldContainSrcAttribute() { + $(".inventory_details_img img") + .shouldHave(attributeMatching("src", ".*")); + } + @Test + public void addRemoveAddFlowShouldWork() { + ProductPage page = + openProductPage(); + + page.addToCart() + .removeFromCart() + .addToCart() + .verifyCartBadge("1"); + } + +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/testng.xml b/java-reporter-testng-selenide/testng.xml new file mode 100644 index 0000000..ddd09ab --- /dev/null +++ b/java-reporter-testng-selenide/testng.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 684962dbae31f632bf5c33d6a48cc7c3056a3fd8 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 15 May 2026 21:56:41 +0300 Subject: [PATCH 2/2] Issue-114 Fixed PR comments --- java-reporter-testng-selenide/README.md | 62 +++++++++++++++++++ .../main/java/io/testomat/config/Urls.java | 15 +++++ .../main/java/io/testomat/pages/CartPage.java | 12 +++- .../java/io/testomat/pages/InventoryPage.java | 38 +++++++++++- .../java/io/testomat/pages/LoginPage.java | 4 +- .../java/io/testomat/pages/ProductPage.java | 13 +++- .../io/testomat/steps/InventorySteps.java | 13 ++++ .../java/io/testomat/steps/LoginSteps.java | 6 ++ .../src/test/java/tests/InventoryTest.java | 25 +++++--- .../src/test/java/tests/LoginTest.java | 26 ++++---- .../test/java/tests/ProductDetailsTest.java | 15 +++-- java-reporter-testng-selenide/testng.xml | 2 + 12 files changed, 192 insertions(+), 39 deletions(-) create mode 100644 java-reporter-testng-selenide/README.md create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/config/Urls.java create mode 100644 java-reporter-testng-selenide/src/main/java/io/testomat/steps/InventorySteps.java diff --git a/java-reporter-testng-selenide/README.md b/java-reporter-testng-selenide/README.md new file mode 100644 index 0000000..0276e71 --- /dev/null +++ b/java-reporter-testng-selenide/README.md @@ -0,0 +1,62 @@ +# Java reporter integration with TestNG + +## Overview + +This simple demo shows how Testomat.io Java reporter works in your project. + +## Installation + +1. Clone the repository + +```sh + git clone https://github.com/testomatio/examples.git + ``` +2. Change the directory + +```sh + cd java-reporter-testng +``` +3. Install dependencies with test skip + +```sh + mvn clean install -DskipTests +``` + + +## Configurations + +**By default, the library runs with properties default values except `testomatio.api.key` and `testomatio.listening`** + +![properties img](img/properties.png) + +Add your project API key to the `testomatio.properties` file ad `testomatio.api.key` + +## Configure TestNG Before Running + +Before running tests, make sure your TestNG suites are configured in the testng.xml file. +Runs test methods in parallel using 7 threads simultaneously. +```xml + + + + + + + + + + + + + +``` + +## Run + +Run tests with + +```bash + mvn test -Dtestomatio.api.key=tstmt_key #if you did not provide it in the `testomatio.properties` file +``` + +where `tstmt_key` is your Testomat.io key from a particular project. diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/config/Urls.java b/java-reporter-testng-selenide/src/main/java/io/testomat/config/Urls.java new file mode 100644 index 0000000..343be51 --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/config/Urls.java @@ -0,0 +1,15 @@ +package io.testomat.config; + +public final class Urls { + + public static final String BASE_URL = "https://www.saucedemo.com/"; + public static final String INVENTORY_URL = BASE_URL + "inventory.html"; + private static final String INVENTORY_ITEM_PATTERN = BASE_URL + INVENTORY_URL + "?id=%s"; + + private Urls() { + } + + public static String inventoryItemUrl(int id) { + return String.format(INVENTORY_ITEM_PATTERN, id); + } +} \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java index b879a78..40fc2c5 100644 --- a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/CartPage.java @@ -1,8 +1,11 @@ package io.testomat.pages; import com.codeborne.selenide.ElementsCollection; +import com.codeborne.selenide.SelenideElement; import static com.codeborne.selenide.CollectionCondition.sizeGreaterThan; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$$; public class CartPage { @@ -10,15 +13,20 @@ public class CartPage { private final ElementsCollection cartItems = $$(".cart_item"); - public CartPage verifyItemsExist() { + private final SelenideElement cartLink = + $(".shopping_cart_link"); + public CartPage verifyItemsExist() { cartItems.shouldHave(sizeGreaterThan(0)); return this; } public int getItemsCount() { - return cartItems.size(); } + + public void cartLinkShouldVisible() { + cartLink.shouldBe(visible); + } } \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java index ef15228..2e03567 100644 --- a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/InventoryPage.java @@ -41,6 +41,18 @@ public class InventoryPage { private final SelenideElement logoutLink = $("#logout_sidebar_link"); + private final ElementsCollection inventoryItemsImgs = + $$(".inventory_item_img img"); + + private final SelenideElement inventoryDetailsImgs = + $(".inventory_details_img img"); + + private final SelenideElement inventoryDetailsName = + $(".inventory_details_name"); + + private final SelenideElement inventoryList = + $(".inventory_list"); + public InventoryPage verifyPageLoaded() { title.shouldHave(text("Products")); inventoryItems.shouldHave(sizeGreaterThan(0)); @@ -59,7 +71,6 @@ public List getItemNames() { } public List getItemPrices() { - return itemPrices.texts(); } @@ -71,7 +82,6 @@ public InventoryPage sortBy(String value) { } public InventoryPage addFirstItemToCart() { - addToCartButtons.first().click(); return this; @@ -99,4 +109,28 @@ public LoginPage logout() { return new LoginPage(); } + + public ElementsCollection getInventoryItemsImgs() { + return inventoryItemsImgs; + } + + public SelenideElement getInventoryDetailsImgs() { + return inventoryDetailsImgs; + } + + public SelenideElement getInventoryDetailsName() { + return inventoryDetailsName; + } + + public void inventoryListShouldVisible() { + inventoryList.shouldBe(visible); + } + + public ElementsCollection getCartButtons() { + return addToCartButtons; + } + + public ElementsCollection getItemPriceElements() { + return itemPrices; + } } \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java index dd17802..6d6475c 100644 --- a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/LoginPage.java @@ -1,6 +1,7 @@ package io.testomat.pages; import com.codeborne.selenide.SelenideElement; +import io.testomat.config.Urls; import static com.codeborne.selenide.Condition.text; import static com.codeborne.selenide.Condition.visible; @@ -9,7 +10,6 @@ public class LoginPage { - private final SelenideElement rootContainer = $("[data-test='login-container']"); private final SelenideElement usernameInput = $("#user-name"); private final SelenideElement passwordInput = $("#password"); private final SelenideElement loginButton = $("#login-button"); @@ -17,7 +17,7 @@ public class LoginPage { private final SelenideElement errorMessage = $("h3[data-test='error']"); public LoginPage openPage() { - open("https://www.saucedemo.com/"); + open(Urls.BASE_URL); return this; } diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java index 25471ad..60136a8 100644 --- a/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/pages/ProductPage.java @@ -31,6 +31,9 @@ public class ProductPage { private final SelenideElement cartBadge = $(".shopping_cart_badge"); + private final SelenideElement productSortContainer = + $(".product_sort_container"); + public ProductPage verifyPageLoaded() { productName.shouldBe(visible); @@ -78,16 +81,22 @@ public ProductPage removeFromCart() { } public ProductPage verifyCartBadge(String count) { - cartBadge.shouldHave(text(count)); return this; } public InventoryPage clickBackButton() { - backButton.click(); return new InventoryPage(); } + + public SelenideElement getCartBadge() { + return cartBadge; + } + + public void productSortContainerShouldVisible() { + productSortContainer.shouldBe(visible); + } } \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/steps/InventorySteps.java b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/InventorySteps.java new file mode 100644 index 0000000..83695fd --- /dev/null +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/InventorySteps.java @@ -0,0 +1,13 @@ +package io.testomat.steps; + +import io.testomat.pages.ProductPage; + +public class InventorySteps { + + public String getCartBadgeAmount() { + return new ProductPage() + .getCartBadge() + .getText(); + } + +} diff --git a/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java index 8f1f533..923c33d 100644 --- a/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java +++ b/java-reporter-testng-selenide/src/main/java/io/testomat/steps/LoginSteps.java @@ -1,5 +1,6 @@ package io.testomat.steps; +import com.codeborne.selenide.SelenideElement; import io.testomat.data.Users; import io.testomat.pages.InventoryPage; import io.testomat.pages.LoginPage; @@ -24,4 +25,9 @@ public static InventoryPage loginAs(String username) { .clickLogin() .verifyPageLoaded(); } + + public SelenideElement getInventoryDetailsName() { + return new InventoryPage().getInventoryDetailsName(); + } + } \ No newline at end of file diff --git a/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java b/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java index 43ae1c2..8886841 100644 --- a/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java +++ b/java-reporter-testng-selenide/src/test/java/tests/InventoryTest.java @@ -1,16 +1,15 @@ package tests; import static com.codeborne.selenide.Condition.empty; -import static com.codeborne.selenide.Condition.text; import static com.codeborne.selenide.Condition.visible; -import static com.codeborne.selenide.Selenide.$; -import static com.codeborne.selenide.Selenide.$$; import static com.codeborne.selenide.Selenide.open; import static com.codeborne.selenide.Selenide.refresh; import static com.codeborne.selenide.Selenide.webdriver; import static com.codeborne.selenide.WebDriverConditions.url; import base.BaseTest; +import io.testomat.config.Urls; +import io.testomat.steps.InventorySteps; import java.util.Comparator; import java.util.List; import org.testng.Assert; @@ -122,8 +121,8 @@ public void productsShouldBeSortedByPriceLowToHigh() { @Test public void userShouldNotAccessInventoryWithoutLogin() { - open("https://www.saucedemo.com/inventory.html"); - webdriver().shouldHave(url("https://www.saucedemo.com/")); + open(Urls.INVENTORY_URL); + webdriver().shouldHave(url(Urls.BASE_URL)); } @Test @@ -149,13 +148,17 @@ public void cartShouldBeEmptyInitially() { @Test public void allProductImagesShouldBeVisible() { - $$(".inventory_item_img img").forEach(img -> img.shouldBe(visible)); + new InventoryPage() + .getInventoryItemsImgs() + .forEach(img -> + img.shouldBe(visible)); } @Test public void addToCartButtonsShouldBeVisible() { LoginSteps.loginAsStandardUser(); - $$("button[id^='add-to-cart']") + new InventoryPage() + .getCartButtons() .forEach(btn -> btn.shouldBe(visible)); } @@ -163,7 +166,8 @@ public void addToCartButtonsShouldBeVisible() { @Test public void allPricesShouldBeVisible() { LoginSteps.loginAsStandardUser(); - $$(".inventory_item_price") + new InventoryPage() + .getItemPriceElements() .forEach(price -> price.shouldNotBe(empty) ); @@ -175,7 +179,8 @@ public void cartShouldPersistAfterRefresh() { .addFirstItemToCart(); refresh(); - $(".shopping_cart_badge") - .shouldHave(text("1")); + Assert.assertEquals( + new InventorySteps().getCartBadgeAmount(), + "1"); } } diff --git a/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java b/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java index 660cfca..7811169 100644 --- a/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java +++ b/java-reporter-testng-selenide/src/test/java/tests/LoginTest.java @@ -2,12 +2,15 @@ import static com.codeborne.selenide.Condition.attributeMatching; import static com.codeborne.selenide.Condition.visible; -import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$$; import static com.codeborne.selenide.Selenide.open; import base.BaseTest; +import io.testomat.config.Urls; import io.testomat.data.Users; +import io.testomat.pages.CartPage; +import io.testomat.pages.InventoryPage; +import io.testomat.pages.ProductPage; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -53,7 +56,6 @@ public void lockedUserShouldSeeError() { @Test public void performanceUserShouldLoginSuccessfully() { - long start = System.currentTimeMillis(); new LoginPage() @@ -63,12 +65,9 @@ public void performanceUserShouldLoginSuccessfully() { .clickLogin() .verifyPageLoaded(); - long elapsed = - System.currentTimeMillis() - start; + long elapsed = System.currentTimeMillis() - start; - Assert.assertTrue( - elapsed > 2000 - ); + Assert.assertTrue(elapsed > 2000); } @Test @@ -110,8 +109,8 @@ public void problemUserCanStillAddItemsToCart() { @Test public void errorUserShouldNavigateToProductPage() { LoginSteps.loginAs(Users.ERROR_USER); - open("https://www.saucedemo.com/inventory-item.html?id=4"); - $(".inventory_details_name").shouldBe(visible); + open(Urls.inventoryItemUrl(4)); + new LoginSteps().getInventoryDetailsName().shouldBe(visible); } @Test @@ -123,11 +122,8 @@ public void visualUserShouldOpenInventoryPage() { public void visualUserShouldSeeMainElements() { LoginSteps.loginAs(Users.VISUAL_USER); - $(".inventory_list").shouldBe(visible); - $(".shopping_cart_link").shouldBe(visible); - $(".product_sort_container").shouldBe(visible); + new InventoryPage().inventoryListShouldVisible(); + new CartPage().cartLinkShouldVisible(); + new ProductPage().productSortContainerShouldVisible(); } - - - } diff --git a/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java b/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java index 27108f5..013ecc9 100644 --- a/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java +++ b/java-reporter-testng-selenide/src/test/java/tests/ProductDetailsTest.java @@ -1,6 +1,8 @@ package tests; import base.BaseTest; +import io.testomat.config.Urls; +import io.testomat.pages.InventoryPage; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -8,7 +10,6 @@ import io.testomat.pages.ProductPage; import static com.codeborne.selenide.Condition.attributeMatching; -import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.open; import static com.codeborne.selenide.Selenide.refresh; import static com.codeborne.selenide.Selenide.webdriver; @@ -26,7 +27,7 @@ public void login() { } private ProductPage openProductPage() { - open("https://www.saucedemo.com/inventory-item.html?id=4"); + open(Urls.inventoryItemUrl(7)); return new ProductPage() .verifyPageLoaded(); @@ -119,15 +120,15 @@ public void pageShouldBeRefreshSafe() { @Test public void userShouldNotAccessProductPageWithoutLogin() { - open("https://www.saucedemo.com/inventory-item.html?id=4"); + open(Urls.inventoryItemUrl(3)); webdriver().shouldHave( - url("https://www.saucedemo.com/") + url(Urls.BASE_URL) ); } @Test public void invalidProductIdShouldNotCrashApplication() { - open("https://www.saucedemo.com/inventory-item.html?id=999"); + open(Urls.inventoryItemUrl(999)); } @Test @@ -143,9 +144,11 @@ public void correctProductShouldBeDisplayed() { @Test public void imageShouldContainSrcAttribute() { - $(".inventory_details_img img") + new InventoryPage() + .getInventoryDetailsImgs() .shouldHave(attributeMatching("src", ".*")); } + @Test public void addRemoveAddFlowShouldWork() { ProductPage page = diff --git a/java-reporter-testng-selenide/testng.xml b/java-reporter-testng-selenide/testng.xml index ddd09ab..f8d15cc 100644 --- a/java-reporter-testng-selenide/testng.xml +++ b/java-reporter-testng-selenide/testng.xml @@ -4,7 +4,9 @@ + +