From d7f8c4b9f622acd7a5985baaa936f45d9b6948a6 Mon Sep 17 00:00:00 2001 From: Roman Burlaka Date: Tue, 14 Oct 2025 20:18:24 +0200 Subject: [PATCH 1/3] testLoadDelay for FirstRowTest --- pom.xml | 8 +-- src/test/java/ui/testing/page/HomePage.java | 1 + .../java/ui/testing/page/LoadDelayPage.java | 22 ++++++ .../java/ui/testing/test/FirstRowTest.java | 68 ++++++++++++++----- .../java/ui/testing/utils/TestListener.java | 14 ++++ src/test/resources/suites/suiteRow1.xml | 0 6 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 src/test/java/ui/testing/page/LoadDelayPage.java create mode 100644 src/test/resources/suites/suiteRow1.xml diff --git a/pom.xml b/pom.xml index 9f6a7cb..9002ade 100644 --- a/pom.xml +++ b/pom.xml @@ -9,10 +9,10 @@ 1.0-SNAPSHOT - - 17 - 17 - 17 + + 21 + 21 + 21 UTF-8 diff --git a/src/test/java/ui/testing/page/HomePage.java b/src/test/java/ui/testing/page/HomePage.java index 781d5be..44a5e83 100644 --- a/src/test/java/ui/testing/page/HomePage.java +++ b/src/test/java/ui/testing/page/HomePage.java @@ -18,6 +18,7 @@ public HomePage(WebDriver driver) { @Step("Go to {name}") public T goToPage(String name, T page) { + getDriver().findElement(By.xpath(String.format("//h3//a[text()='%s']", name))).click(); Reporter.log("Title is : " + page.toString()); return page; diff --git a/src/test/java/ui/testing/page/LoadDelayPage.java b/src/test/java/ui/testing/page/LoadDelayPage.java new file mode 100644 index 0000000..5e1c010 --- /dev/null +++ b/src/test/java/ui/testing/page/LoadDelayPage.java @@ -0,0 +1,22 @@ +package ui.testing.page; + +import io.qameta.allure.Step; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import ui.testing.base.BasePage; + +public class LoadDelayPage extends BasePage { + public LoadDelayPage(WebDriver driver) { + super(driver); + } + + @Step("Wait until button will be presence on page") + public String confirmAppearedButton() { + WebElement btnAppeared = getDriver().findElement(By.xpath("//button[@class = 'btn btn-primary']")); + wait4().until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath("//button[@class = 'btn btn-primary']"))); + + return btnAppeared.getText(); + } +} diff --git a/src/test/java/ui/testing/test/FirstRowTest.java b/src/test/java/ui/testing/test/FirstRowTest.java index 38ad91f..ccbfa78 100644 --- a/src/test/java/ui/testing/test/FirstRowTest.java +++ b/src/test/java/ui/testing/test/FirstRowTest.java @@ -1,36 +1,50 @@ package ui.testing.test; +import io.qameta.allure.*; import org.testng.Assert; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import ui.testing.base.BaseTest; -import ui.testing.page.ClassAttributePage; -import ui.testing.page.DynamicIdPage; -import ui.testing.page.HiddenLayersPage; -import ui.testing.page.HomePage; +import ui.testing.page.*; @Listeners(ui.testing.utils.TestListener.class) +@Epic("UI Testing Playground") +@Feature("Elements") +@Owner("RomanB") public class FirstRowTest extends BaseTest { - @Test + @Test(description = "Dynamic ID - Verify that element ID changes after refresh") + @Story("Dynamic ID") + @Severity(SeverityLevel.NORMAL) public void testDynamicId() { String id = new HomePage(driver) .goToPage("Dynamic ID", new DynamicIdPage(driver)) .getIdButton(); - driver.navigate().refresh(); + Allure.step("Capture button id before refresh: " + id); + Allure.addAttachment("Before refresh: ", id); + driver.navigate().refresh(); String idNew = new DynamicIdPage(driver) .getIdButton(); + Allure.step("Capture button id after refresh: " + idNew); + Allure.addAttachment("After refresh: ", idNew); + Assert.assertNotEquals(id, idNew, "ID должен меняться после обновления страницы"); } - @Test + @Test(description = "Class Attribute - Verify that class-based XPath is well formed") + @Story("Class Attribute") + @Severity(SeverityLevel.MINOR) public void testClassAttribute() { String alert = new HomePage(driver) .goToPage("Class Attribute", new ClassAttributePage(driver)) .getAlert(); + + Allure.step("Alert text captured: " + alert); + Allure.addAttachment("Alert text: ", alert); + String attributeClassButton = new ClassAttributePage(driver) .getAttributeClass(); @@ -39,16 +53,36 @@ public void testClassAttribute() { softAssert().assertAll(); } - @Test - public void testHiddenLayers() { - String firstClick = new HomePage(driver) - .goToPage("Hidden Layers", new HiddenLayersPage(driver)) - .notAllowedClickMoreThanOne(); - String secondClick = new HiddenLayersPage(driver) - .notAllowedClickMoreThanOne(); - - softAssert().assertEquals(firstClick, "Green button visible and clicked. Click : true"); - softAssert().assertEquals(secondClick, "Green button is hidden. Click : false"); + @Test(description = "Hidden Layers - Ensure test doesn't click invisible elements") + @Story("Hidden Layers") + @Severity(SeverityLevel.CRITICAL) + public void testHiddenLayers() { + Allure.step("Try first click on green button", () -> { + String firstClick = new HomePage(driver) + .goToPage("Hidden Layers", new HiddenLayersPage(driver)) + .notAllowedClickMoreThanOne(); + softAssert().assertEquals(firstClick, "Green button visible and clicked. Click : true"); + Allure.addAttachment("Green button visible and clicked", firstClick); + }); + Allure.step("Try second click on the green button", () -> { + String secondClick = new HiddenLayersPage(driver) + .notAllowedClickMoreThanOne(); + softAssert().assertEquals(secondClick, "Green button is hidden. Click : false"); + Allure.addAttachment("Green button is hidden", secondClick); + }); softAssert().assertAll(); } + + @Test(description = "Load Delay - Ensure that a test is capable of waiting for a page to load") + @Story("Load Delay") + @Severity(SeverityLevel.CRITICAL) + public void testLoadDelay() { + Allure.step("Go to Load Delay page after page is loaded", ()-> { + String delay = new HomePage(driver) + .goToPage("Load Delay", new LoadDelayPage(driver)) + .confirmAppearedButton(); + softAssert().assertEquals(delay, "Button Appearing After Delay"); + Allure.addAttachment("Button is shown : ", delay); + }); + } } diff --git a/src/test/java/ui/testing/utils/TestListener.java b/src/test/java/ui/testing/utils/TestListener.java index 2330b94..52893bc 100644 --- a/src/test/java/ui/testing/utils/TestListener.java +++ b/src/test/java/ui/testing/utils/TestListener.java @@ -1,5 +1,7 @@ package ui.testing.utils; +import com.codeborne.selenide.impl.Screenshot; +import io.qameta.allure.Allure; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; @@ -7,13 +9,19 @@ import org.testng.ITestResult; import org.testng.Reporter; +import java.util.logging.Logger; + public class TestListener implements ITestListener { String message = ""; + + private static final Logger logger = Logger.getGlobal(); + @Override public void onTestStart(ITestResult result) { message = "🚀 TEST STARTED: " + result.getName() ; Reporter.log(message + "
"); System.out.println(message); + logger.info(message); } @Override @@ -21,6 +29,7 @@ public void onTestSuccess(ITestResult result) { message = "✅ TEST PASSED: " + result.getName(); Reporter.log(message + "
"); System.out.println(message); + logger.info(message); } @Override @@ -29,6 +38,8 @@ public void onTestFailure(ITestResult result) { Reporter.log("Cause: " + result.getThrowable() + "
"); message = "❌ TEST FAILED: " + result.getName(); System.out.println(message); + logger.warning(message); + Allure.addAttachment("Test failed : ", result.getName()); Object testClass = result.getInstance(); try { @@ -42,9 +53,11 @@ public void onTestFailure(ITestResult result) { String base64 = ts.getScreenshotAs(OutputType.BASE64); String imgTag = ""; Reporter.log(imgTag + "
"); + Allure.addAttachment("Screenshot on failure :", imgTag); } } catch (Exception e) { Reporter.log("⚠️ Не удалось получить WebDriver: " + e.getMessage()); + Allure.addAttachment("⚠️ Could not capture screenshot: {}", e.getMessage()); } } @@ -53,5 +66,6 @@ public void onTestSkipped(ITestResult result) { Reporter.log("⏩ SKIPPED: " + result.getName() + "
"); message = "⏩ TEST SKIPPED: " + result.getName(); System.out.println(message); + Allure.step("Skipped test: " + result.getName()); } } \ No newline at end of file diff --git a/src/test/resources/suites/suiteRow1.xml b/src/test/resources/suites/suiteRow1.xml new file mode 100644 index 0000000..e69de29 From 5ebdda3766d2fdb39f8eff04c6f63463b7e9cac8 Mon Sep 17 00:00:00 2001 From: Roman Burlaka Date: Wed, 15 Oct 2025 20:48:48 +0200 Subject: [PATCH 2/3] added ci.yaml --- .github/workflows/ci.yml | 96 +++++++++++++++++++ src/test/java/ui/testing/base/BasePage.java | 2 +- .../java/ui/testing/page/AjaxDataPage.java | 34 +++++++ .../ui/testing/page/ClassAttributePage.java | 2 +- .../java/ui/testing/page/DynamicIdPage.java | 2 +- .../ui/testing/page/HiddenLayersPage.java | 2 +- src/test/java/ui/testing/page/HomePage.java | 2 +- .../java/ui/testing/page/LoadDelayPage.java | 2 +- .../java/ui/testing/test/FirstRowTest.java | 7 +- .../java/ui/testing/test/SecondRowTest.java | 33 +++++++ src/test/resources/suites/suiteRow1.xml | 0 11 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 src/test/java/ui/testing/page/AjaxDataPage.java create mode 100644 src/test/java/ui/testing/test/SecondRowTest.java delete mode 100644 src/test/resources/suites/suiteRow1.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..903672f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,96 @@ +name: Java CI with Maven + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + build: + name: 'Run tests' + runs-on: ubuntu-latest + + steps: + # 1. Cloning + - name: Git clone + uses: actions/checkout@v4 + + # 2. Install Java 21 + - name: Setup JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'corretto' + cache: 'maven' + + # 3. Run tests (not stop workflow if failure ) + - name: Build and run tests with Maven + run: mvn -B -e clean test -Dselenium.headless=true || true + env: + CHROME_OPTIONS: --remote-allow-origins=*;--disable-gpu;--no-sandbox;--disable-dev-shm-usage;--headless=new;--window-size=1920,1080 + + # 4. Output failed tests + - name: Print failed tests + if: failure() + run: | + echo "---- TEST FAILURES ----" + cat target/surefire-reports/*.txt || true + + # 5. Attach screenshots + - name: Attach screenshots + if: always() + uses: actions/upload-artifact@v4 + with: + name: screenshots + path: ${{ github.workspace }}/screenshots + + # 6. Get Allure (gh-pages) + - name: Get Allure history + uses: actions/checkout@v4 + if: always() + continue-on-error: true + with: + ref: gh-pages + path: gh-pages + + # 7. Build Allure report + - name: Build Allure report + uses: simple-elf/allure-report-action@master + if: always() + id: allure-report + with: + allure_results: target/allure-results + gh_pages: gh-pages + allure_report: allure-report + allure_history: allure-history + + # 8. Upload Allure report artifact + - name: Upload Allure report artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-report + path: target/allure-report + + # 9. Check allure-results + - name: Check Allure results + run: ls -la target/allure-results + + # 10. Publish Allure to GitHub Pages + - name: Publish Allure report to Github Pages + if: always() + uses: peaceiris/actions-gh-pages@v2 + env: + PERSONAL_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PUBLISH_BRANCH: gh-pages + PUBLISH_DIR: allure-history + + - name: Upload Allure HTML report as artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-report + path: allure-report diff --git a/src/test/java/ui/testing/base/BasePage.java b/src/test/java/ui/testing/base/BasePage.java index fab884b..619b9ef 100644 --- a/src/test/java/ui/testing/base/BasePage.java +++ b/src/test/java/ui/testing/base/BasePage.java @@ -6,7 +6,7 @@ import java.time.Duration; -public class BasePage { +public abstract class BasePage> { private final WebDriver driver; public BasePage(WebDriver driver) { diff --git a/src/test/java/ui/testing/page/AjaxDataPage.java b/src/test/java/ui/testing/page/AjaxDataPage.java new file mode 100644 index 0000000..da6fc3c --- /dev/null +++ b/src/test/java/ui/testing/page/AjaxDataPage.java @@ -0,0 +1,34 @@ +package ui.testing.page; + +import io.qameta.allure.Step; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import ui.testing.base.BasePage; + +import java.time.Duration; + +public class AjaxDataPage extends BasePage +{ + public AjaxDataPage(WebDriver driver) { + super(driver); + } + @Step("Press the button : {text}") + public AjaxDataPage pressButtonAjaxRequest() { + WebElement btn = getDriver().findElement(By.id("ajaxButton")); + btn.click(); + String text = btn.getText(); + return this; + } + @Step("Waiting until appear loaded label : {text}") + public String waitUntilLoadedLabel() { + WebElement result = new WebDriverWait(getDriver(), Duration.ofSeconds(30)).until(ExpectedConditions.presenceOfElementLocated(By.xpath("//div[@id='content']/p"))); + String text = result.getText(); + + return text; + } + + +} diff --git a/src/test/java/ui/testing/page/ClassAttributePage.java b/src/test/java/ui/testing/page/ClassAttributePage.java index 983cb9c..b83c7fb 100644 --- a/src/test/java/ui/testing/page/ClassAttributePage.java +++ b/src/test/java/ui/testing/page/ClassAttributePage.java @@ -7,7 +7,7 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import ui.testing.base.BasePage; -public class ClassAttributePage extends BasePage { +public class ClassAttributePage extends BasePage { public ClassAttributePage(WebDriver driver) { super(driver); } diff --git a/src/test/java/ui/testing/page/DynamicIdPage.java b/src/test/java/ui/testing/page/DynamicIdPage.java index e5c02a6..683f7f4 100644 --- a/src/test/java/ui/testing/page/DynamicIdPage.java +++ b/src/test/java/ui/testing/page/DynamicIdPage.java @@ -5,7 +5,7 @@ import org.openqa.selenium.WebElement; import ui.testing.base.BasePage; -public class DynamicIdPage extends BasePage { +public class DynamicIdPage extends BasePage { public DynamicIdPage(WebDriver driver) { super(driver); } diff --git a/src/test/java/ui/testing/page/HiddenLayersPage.java b/src/test/java/ui/testing/page/HiddenLayersPage.java index 53331ba..f76028d 100644 --- a/src/test/java/ui/testing/page/HiddenLayersPage.java +++ b/src/test/java/ui/testing/page/HiddenLayersPage.java @@ -7,7 +7,7 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import ui.testing.base.BasePage; -public class HiddenLayersPage extends BasePage { +public class HiddenLayersPage extends BasePage { public HiddenLayersPage(WebDriver driver) { super(driver); } diff --git a/src/test/java/ui/testing/page/HomePage.java b/src/test/java/ui/testing/page/HomePage.java index 44a5e83..c5acd79 100644 --- a/src/test/java/ui/testing/page/HomePage.java +++ b/src/test/java/ui/testing/page/HomePage.java @@ -8,7 +8,7 @@ import org.testng.Reporter; import ui.testing.base.BasePage; -public class HomePage extends BasePage { +public class HomePage extends BasePage { public HomePage(WebDriver driver) { super(driver); } diff --git a/src/test/java/ui/testing/page/LoadDelayPage.java b/src/test/java/ui/testing/page/LoadDelayPage.java index 5e1c010..5a88e95 100644 --- a/src/test/java/ui/testing/page/LoadDelayPage.java +++ b/src/test/java/ui/testing/page/LoadDelayPage.java @@ -7,7 +7,7 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import ui.testing.base.BasePage; -public class LoadDelayPage extends BasePage { +public class LoadDelayPage extends BasePage { public LoadDelayPage(WebDriver driver) { super(driver); } diff --git a/src/test/java/ui/testing/test/FirstRowTest.java b/src/test/java/ui/testing/test/FirstRowTest.java index ccbfa78..4002abc 100644 --- a/src/test/java/ui/testing/test/FirstRowTest.java +++ b/src/test/java/ui/testing/test/FirstRowTest.java @@ -34,7 +34,7 @@ public void testDynamicId() { Assert.assertNotEquals(id, idNew, "ID должен меняться после обновления страницы"); } - @Test(description = "Class Attribute - Verify that class-based XPath is well formed") + @Test @Story("Class Attribute") @Severity(SeverityLevel.MINOR) public void testClassAttribute() { @@ -53,7 +53,7 @@ public void testClassAttribute() { softAssert().assertAll(); } - @Test(description = "Hidden Layers - Ensure test doesn't click invisible elements") + @Test @Story("Hidden Layers") @Severity(SeverityLevel.CRITICAL) public void testHiddenLayers() { @@ -73,7 +73,7 @@ public void testHiddenLayers() { softAssert().assertAll(); } - @Test(description = "Load Delay - Ensure that a test is capable of waiting for a page to load") + @Test() @Story("Load Delay") @Severity(SeverityLevel.CRITICAL) public void testLoadDelay() { @@ -85,4 +85,5 @@ public void testLoadDelay() { Allure.addAttachment("Button is shown : ", delay); }); } + } diff --git a/src/test/java/ui/testing/test/SecondRowTest.java b/src/test/java/ui/testing/test/SecondRowTest.java new file mode 100644 index 0000000..f34fed1 --- /dev/null +++ b/src/test/java/ui/testing/test/SecondRowTest.java @@ -0,0 +1,33 @@ +package ui.testing.test; + +import io.qameta.allure.*; +import org.openqa.selenium.WebDriver; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; +import ui.testing.base.BasePage; +import ui.testing.base.BaseTest; +import ui.testing.page.AjaxDataPage; +import ui.testing.page.HomePage; +import ui.testing.page.LoadDelayPage; + +@Listeners(ui.testing.utils.TestListener.class) +@Epic("UI Testing Playground") +@Feature("Elements") +@Owner("RomanB") +public class SecondRowTest extends BaseTest { + + @Test + @Story("AJAX Data") + @Severity(SeverityLevel.CRITICAL) + public void testAjaxData() { + Allure.step("Go to AjaxDataPage and wait for all element loaded", ()-> { + String ajax = new HomePage(driver) + .goToPage("AJAX Data", new AjaxDataPage(driver)) + .pressButtonAjaxRequest() + .waitUntilLoadedLabel(); + + softAssert().assertEquals(ajax, "Data loaded with AJAX get request."); + Allure.addAttachment("Label is loaded : ", ajax); + }); + } +} diff --git a/src/test/resources/suites/suiteRow1.xml b/src/test/resources/suites/suiteRow1.xml deleted file mode 100644 index e69de29..0000000 From abde434f6c2adf8212eb20846bb94da28703b9d1 Mon Sep 17 00:00:00 2001 From: Roman Burlaka Date: Wed, 15 Oct 2025 20:55:40 +0200 Subject: [PATCH 3/3] refactor @BeforeTest in BaseTest --- src/test/java/ui/testing/base/BaseTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/java/ui/testing/base/BaseTest.java b/src/test/java/ui/testing/base/BaseTest.java index d5aa13a..2423099 100644 --- a/src/test/java/ui/testing/base/BaseTest.java +++ b/src/test/java/ui/testing/base/BaseTest.java @@ -1,5 +1,6 @@ package ui.testing.base; +import io.github.bonigarcia.wdm.WebDriverManager; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; @@ -16,8 +17,19 @@ public SoftAssert softAssert() { @BeforeMethod public void setUp() { + WebDriverManager.chromedriver().setup(); ChromeOptions chromeOptions = new ChromeOptions(); + chromeOptions.addArguments("--headless"); // стабильно на Windows chromeOptions.addArguments("--window-size=1920,1080"); + chromeOptions.addArguments("--disable-gpu"); + chromeOptions.addArguments("--remote-allow-origins=*"); + + if (!System.getProperty("os.name").toLowerCase().contains("win")) { + chromeOptions.addArguments("--no-sandbox", "--disable-dev-shm-usage"); + chromeOptions.addArguments("--user-data-dir=/tmp/chrome-profile-" + System.currentTimeMillis()); + } else { + chromeOptions.addArguments("--user-data-dir=" + System.getProperty("java.io.tmpdir") + "chrome-profile-" + System.currentTimeMillis()); + } driver = new ChromeDriver(chromeOptions); driver.get("http://uitestingplayground.com/"); }