diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..5bc1949
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,50 @@
+name: Run tests
+env:
+ ALLURE_VERSION: "2.10.0"
+
+on:
+ push:
+ branches:
+ - main
+
+ pull_request:
+ branches:
+ - main
+ release:
+ types:
+ - created
+
+jobs:
+ build:
+ name: Tests on JDK
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ java: [ 11, 17 ]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ matrix.java }}
+ java-package: jdk
+ distribution: 'temurin'
+ cache: 'maven'
+
+ - name: Build with Maven
+ id: build
+ run: mvn clean install -DskipTests -ntp
+
+ - name: Tests
+ id: functests
+ timeout-minutes: 10
+ continue-on-error: true
+ run: mvn test -ntp
+
+ - name: Check tests are passed
+ if: ${{ steps.functests.outcome != 'success' }}
+ run: |
+ echo Tests result: ${{ steps.functests.outcome }}
+ exit 1
diff --git a/.gitignore b/.gitignore
index 524f096..45d878c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,12 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
+
+# Idea files
+*.iml
+.idea/
+target/
+
+# Allure files
+.allure/
+allure-results/
\ No newline at end of file
diff --git a/README.md b/README.md
index 18acb46..90d4105 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,23 @@
-# jdn-template-selenium
-Template project for JDN with Selenium
+# Selenium Testng empty template
+
+Empty template for Test Automation project with Selenium
+
+# Instruction:
+
+1. Download template and unpack in appropriate folder
+
+2. Open project in IDE (for example IntelliJIdea)
+
+3. Reporting: Allure is enabled, after running tests just run **allure:serve** in maven plugins (allure should be
+ installed locally)
+
+4. Parameters that can be changed: configure the headless mode, base URL and browser type (chrome/safari/ie/firefox/edg)
+ by updating the properties in the pom.xml file.
+
+5. TestNg Retry and before after listeners: You can also modify rules of retry tests (now it is 1 retry for each test)
+ and actions before/after all tests (now it prints test name and result) in **testng** folder
+
+6. pages generated by JDN should be places in src/main/java/pages package
+
+7. Each page object class must be inherited from BasePage, including one of the mandatory paremeters is PATH describing
+ the URI of the page e.g. in http://example.com/login PATH = "/login".
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2b6d909
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,132 @@
+
+ 4.0.0
+
+ com.epam.jdi
+ jdn-template-selenium
+ 1.0.0
+ Template: Selenium TestNG Testing project
+
+
+ 7.8.0
+ 2.7
+ 2.22.0
+ 3.141.59
+ 1.18.22
+ 5.6.3
+ 2.13.6
+ 2.12.0
+ 3.8.1
+ 2.22.2
+ 11
+ 11
+
+ https://example.com
+ true
+ chrome
+
+
+
+
+
+ org.apache.commons
+ commons-configuration2
+ ${commons.config.version}
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.core.version}
+
+
+
+ org.seleniumhq.selenium
+ selenium-java
+ ${selenium.java.version}
+
+
+
+ org.testng
+ testng
+ ${testng.version}
+ test
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ test
+
+
+
+ io.github.bonigarcia
+ webdrivermanager
+ ${webdrivermanager.version}
+
+
+
+ io.qameta.allure
+ allure-testng
+ ${allure.testng.version}
+
+
+
+
+
+
+ io.qameta.allure
+ allure-maven
+ 2.12.0
+
+ 2.13.6
+
+
+ allure.results.directory
+ target
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ ${maven.compiler.target}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+ ${project.build.directory}/allure-results
+
+
+ src/test/resources/testng.xml
+
+
+
+ listener
+ io.qameta.allure.testng.AllureTestNg
+
+
+
+
+
+
+
+ src/test/resources
+
+
+ src/test/resources
+ true
+
+
+
+
+
diff --git a/src/main/java/configuration/Configuration.java b/src/main/java/configuration/Configuration.java
new file mode 100644
index 0000000..1d1143c
--- /dev/null
+++ b/src/main/java/configuration/Configuration.java
@@ -0,0 +1,39 @@
+package configuration;
+
+import static java.lang.Boolean.parseBoolean;
+
+public class Configuration {
+ private static final Configuration INSTANCE = new Configuration();
+
+ private final String baseUrl;
+ private final String browser;
+ private final boolean headless;
+
+ private Configuration() {
+ // Initialize configuration properties
+ this.baseUrl = getProperty("base.url", "https://example.com");
+ this.browser = getProperty("browser", "chrome");
+ this.headless = parseBoolean(getProperty("headless", "true"));
+ }
+
+ public static Configuration getInstance() {
+ return INSTANCE;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public String getBrowser() {
+ return browser;
+ }
+
+ public boolean isHeadless() {
+ return headless;
+ }
+
+ private String getProperty(String propertyName, String defaultValue) {
+ return System.getProperty(propertyName, defaultValue);
+ }
+}
+
diff --git a/src/main/java/pages/BasePage.java b/src/main/java/pages/BasePage.java
new file mode 100644
index 0000000..601fb64
--- /dev/null
+++ b/src/main/java/pages/BasePage.java
@@ -0,0 +1,62 @@
+package pages;
+
+import java.util.List;
+
+import configuration.Configuration;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.PageFactory;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+public abstract class BasePage {
+
+ public final WebDriver driver;
+ public final int DEFAULT_TIMEOUT = 30;
+ protected String baseUrl;
+ protected String path;
+
+ public BasePage(WebDriver driver, String path) {
+ this.driver = driver;
+ this.path = path;
+ baseUrl = Configuration.getInstance()
+ .getBaseUrl();
+ PageFactory.initElements(driver, this);
+ }
+
+ public void waitUntilElementVisible(WebElement webElement) {
+ WebDriverWait wait = new WebDriverWait(driver, DEFAULT_TIMEOUT);
+ wait.until(ExpectedConditions.visibilityOf(webElement));
+ }
+
+ public void waitUntilElementClickable(WebElement webElement) {
+ WebDriverWait wait = new WebDriverWait(driver, DEFAULT_TIMEOUT);
+ wait.until(ExpectedConditions.elementToBeClickable(webElement));
+ }
+
+ public void click(WebElement webElement) {
+ waitUntilElementClickable(webElement);
+ webElement.click();
+ }
+
+ public WebElement getElement(WebElement webElement) {
+ waitUntilElementVisible(webElement);
+ return webElement;
+ }
+
+ public List getElements(List webElements) {
+ for (WebElement element : webElements) {
+ waitUntilElementVisible(element);
+ }
+ return webElements;
+ }
+
+ public void sendKeys(WebElement webElement, String text) {
+ waitUntilElementVisible(webElement);
+ webElement.sendKeys(text);
+ }
+
+ public void open() {
+ driver.get(baseUrl + this.path);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/pages/HomePage.java b/src/main/java/pages/HomePage.java
new file mode 100644
index 0000000..a94a6d1
--- /dev/null
+++ b/src/main/java/pages/HomePage.java
@@ -0,0 +1,16 @@
+package pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+// This is an example of Page Object definition. To be removed
+public class HomePage extends BasePage {
+ private static final String PATH = "/";
+ @FindBy(css = "p > a")
+ public WebElement link;
+
+ public HomePage(final WebDriver driver) {
+ super(driver, PATH);
+ }
+}
diff --git a/src/main/java/utilities/DriverFactory.java b/src/main/java/utilities/DriverFactory.java
new file mode 100644
index 0000000..d1000b5
--- /dev/null
+++ b/src/main/java/utilities/DriverFactory.java
@@ -0,0 +1,64 @@
+package utilities;
+
+import configuration.Configuration;
+import io.github.bonigarcia.wdm.WebDriverManager;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.edge.EdgeDriver;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.ie.InternetExplorerDriver;
+import org.openqa.selenium.safari.SafariDriver;
+
+public class DriverFactory {
+
+ public static final String CHROME = "chrome";
+ public static final String FIREFOX = "firefox";
+ public static final String IE = "ie";
+ public static final String EDGE = "edge";
+ public static final String SAFARI = "safari";
+
+ public static WebDriver getDriver(String browserName) {
+ if (browserName == null) {
+ WebDriverManager.chromedriver()
+ .setup();
+ return new ChromeDriver();
+ }
+ switch (browserName.toLowerCase()) {
+ case CHROME:
+ boolean headlessMode = Configuration.getInstance()
+ .isHeadless();
+ ChromeOptions chromeOptions = new ChromeOptions();
+ chromeOptions.addArguments("--no-sandbox");
+ chromeOptions.addArguments("--window-size=1920x1080");
+ chromeOptions.addArguments("disable-infobars");
+ chromeOptions.addArguments("--disable-extensions");
+ chromeOptions.addArguments("--disable-gpu");
+ chromeOptions.addArguments("--disable-dev-shm-usage");
+ if (headlessMode) {
+ chromeOptions.addArguments("--headless");
+ }
+ WebDriverManager.chromedriver()
+ .setup();
+ return new ChromeDriver(chromeOptions);
+ case FIREFOX:
+ WebDriverManager.firefoxdriver()
+ .setup();
+ return new FirefoxDriver();
+ case IE:
+ WebDriverManager.iedriver()
+ .setup();
+ return new InternetExplorerDriver();
+ case EDGE:
+ WebDriverManager.edgedriver()
+ .setup();
+ return new EdgeDriver();
+ case SAFARI:
+ return new SafariDriver();
+ default:
+ WebDriverManager.chromedriver()
+ .setup();
+ return new ChromeDriver();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/test/BaseTest.java b/src/test/java/test/BaseTest.java
new file mode 100644
index 0000000..349368e
--- /dev/null
+++ b/src/test/java/test/BaseTest.java
@@ -0,0 +1,35 @@
+package test;
+
+import configuration.Configuration;
+import org.openqa.selenium.WebDriver;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Listeners;
+import utilities.DriverFactory;
+
+@Listeners()
+public abstract class BaseTest {
+
+ protected WebDriver driver;
+ protected String baseUrl;
+
+ @BeforeMethod
+ public void setUp() {
+ baseUrl = Configuration.getInstance()
+ .getBaseUrl();
+ driver = DriverFactory.getDriver(Configuration.getInstance()
+ .getBrowser());
+ driver.manage()
+ .window()
+ .maximize();
+ driver.get(baseUrl);
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ }
+}
+
diff --git a/src/test/java/test/ExampleTests.java b/src/test/java/test/ExampleTests.java
new file mode 100644
index 0000000..c96ff80
--- /dev/null
+++ b/src/test/java/test/ExampleTests.java
@@ -0,0 +1,21 @@
+package test;
+
+import io.qameta.allure.Description;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import pages.HomePage;
+
+public class ExampleTests extends BaseTest {
+ private HomePage homePage;
+
+ @BeforeMethod
+ public void setUpPage() {
+ homePage = new HomePage(driver);
+ }
+
+ @Test(description = "Example test")
+ public void testMethod() {
+ homePage.open();
+ homePage.link.click();
+ }
+}
diff --git a/src/test/java/testng/RetryAnalyzer.java b/src/test/java/testng/RetryAnalyzer.java
new file mode 100644
index 0000000..494a042
--- /dev/null
+++ b/src/test/java/testng/RetryAnalyzer.java
@@ -0,0 +1,19 @@
+package testng;
+
+import org.testng.IRetryAnalyzer;
+import org.testng.ITestResult;
+
+public class RetryAnalyzer implements IRetryAnalyzer {
+
+ private int retryCount = 0;
+ private static final int MAX_RETRY_COUNT = 3;
+
+ @Override
+ public boolean retry(ITestResult iTestResult) {
+ if (retryCount < MAX_RETRY_COUNT) {
+ retryCount++;
+ return true; // Retry the test
+ }
+ return false; // No retry
+ }
+}
diff --git a/src/test/java/testng/RetryTransformer.java b/src/test/java/testng/RetryTransformer.java
new file mode 100644
index 0000000..a0efd14
--- /dev/null
+++ b/src/test/java/testng/RetryTransformer.java
@@ -0,0 +1,15 @@
+package testng;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.testng.IAnnotationTransformer;
+import org.testng.annotations.ITestAnnotation;
+
+public class RetryTransformer implements IAnnotationTransformer {
+
+ @Override
+ public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
+ iTestAnnotation.setRetryAnalyzer(RetryAnalyzer.class);
+ }
+}
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..6b7aec5
--- /dev/null
+++ b/src/test/resources/log4j2.xml
@@ -0,0 +1,61 @@
+
+
+
+
+ jdiEvents
+ ./target/.logs
+
+
+
+
+
+
+
+
+ ${baseDir}/${logFileName}_%d{yyyy-MM-dd__HH-mm}_%i.log
+
+ %d{HH:mm:ss} [%t] %-5level %logger{6} - %msg%n
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/testng.xml b/src/test/resources/testng.xml
new file mode 100644
index 0000000..c50c8bc
--- /dev/null
+++ b/src/test/resources/testng.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file