Skip to content

Commit

Permalink
Automation test flows (#1068)
Browse files Browse the repository at this point in the history
* #1022 - Apply Automation test for Yas project

- Update base form to retrieve web driver from WebDriverFactory.
- Update package name.
- Add plugin org.apache.maven.plugins:maven-surefire-plugin to make test run as command.
- Create test cases for storefront and backoffice flows

* #1022 - Automation test

- add sonar key + revert pom parents

---------

Co-authored-by: Phuoc Nguyen <phuoc.nguyenba@nashtechglobal.com>
Co-authored-by: Chinh Duong <chinh.duongthi@harveynash.vn>
  • Loading branch information
3 people authored Sep 25, 2024
1 parent f671b77 commit 03cb7f7
Show file tree
Hide file tree
Showing 58 changed files with 2,069 additions and 161 deletions.
87 changes: 40 additions & 47 deletions automation-ui/README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,47 @@
# Automation project for YAS application
# 🛠️ Automation Project for YAS Application

## Tentative technologies and frameworks
This project automates tests for the YAS application using modern Java technologies.

- Java 21
- Spring boot 3.2
- Cucumber
- Cucumber-Spring
- Selenium
- Cucumber-Junit
- SonarCloud
## 🧑‍💻 Technologies and Frameworks
- **Java 21**
- **Spring Boot 3.2**
- **Cucumber**
- **Cucumber-Spring**
- **Selenium**
- **Cucumber-JUnit**
- **SonarCloud**

## 🏗️ Local Development Architecture
```mermaid
flowchart TD
TestRunner("Test Runner (JUnit, TestNG)")
FeatureFile("Feature File (.feature)")
StepDefinitions("Step Definitions (using Java)")
Selenium("Selenium WebDriver")
WebApp("Web Application")
## Local development architecture
```
+----------------------------------------+
| **Test Runner** (JUnit, TestNG) |
| - Executes the Cucumber scenarios |
| - Initiates WebDriver & test flow |
+----------------------------------------+
|
v
+----------------------------------------+
| **Feature File** (.feature) |
| - Written in Gherkin language |
| - Defines scenarios (Given, When, Then)|
+----------------------------------------+
|
v
+----------------------------------------+
| **Step Definitions** (using Java) |
| - Maps Gherkin steps to actual code |
| - Uses Selenium WebDriver for actions |
+----------------------------------------+
|
v
+----------------------------------------+
| **Selenium WebDriver** |
| - Automates browser interactions |
| - Opens browser, simulates actions |
| - Fetches web elements, etc. |
+----------------------------------------+
|
v
+----------------------------------------+
| **Web Application** |
| - The actual application under test |
+----------------------------------------+
TestRunner --> FeatureFile
FeatureFile --> StepDefinitions
StepDefinitions --> Selenium
Selenium --> WebApp
```
## 🚀 Getting Started

To run the tests locally, follow the steps below:

### 1. Start the YAS Application
Ensure the YAS application is running in your local environment.

### 2. Running Tests via Maven
You can run the tests in two modes:

## Getting started :
- **Normal Mode**: Run the tests normally with the following command:
```bash
mvn clean test
```

1. Make sure Yas application is up and running.
2. If you want to execute all scenarios in storefront, go to storefront project and execute command: mvn clean test , all test scenarios will be executed. Another way is running JUnitCucumberRunner class using IDE.
- **Headless Mode**:
To run the tests in headless mode, use the following command:
```bash
mvn clean test -Dheadless
```
55 changes: 55 additions & 0 deletions automation-ui/automation-base/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yas</groupId>
<artifactId>automation-ui</artifactId>
<version>${revision}</version>
</parent>
<artifactId>automation-base</artifactId>

<properties>
<sonar.organization>nashtech-garage</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<!-- TODO: request create sonar key -->
<sonar.projectKey>nashtech-garage_yas-automation-ui-base</sonar.projectKey>
</properties>

<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>${cucumber-testng.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yas;

public class AutomationBaseMain {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.yas.automation.ui.form;

import lombok.Getter;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

/**
* Abstract base class for web forms, providing common form functionality.
*/
@Getter
public abstract class BaseForm {

private WebDriver driver;

private BaseForm() {}

public BaseForm(WebDriver driver) {
this.driver = driver;
}

public void submitForm() {
getSubmitBtn().click();
}

public abstract WebElement getSubmitBtn();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.yas.automation.ui.form;

import lombok.Getter;

@Getter
public enum InputType {

TEXT("textService"),
DROPDOWN("dropdownService"),
FILE("fileService"),
CHECKBOX("checkBoxService");

private final String serviceName;

InputType(String serviceName) {
this.serviceName = serviceName;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Cucumber hook managing cucumber test scenarios
*/
public class Hooks {

@Autowired
private WebDriverFactory webDriverFactory;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.springframework.stereotype.Component;

/**
Expand All @@ -28,7 +29,14 @@ public class WebDriverFactory {
*/
public synchronized WebDriver getChromeDriver() {
if (webDriver.get() == null) {
WebDriver webDriver = new ChromeDriver();
ChromeOptions options = new ChromeOptions();
if (System.getProperty("headless") != null
&& System.getProperty("headless").equals("true")) {
options.addArguments("--headless");
options.addArguments("--disable-gpu");
}

WebDriver webDriver = new ChromeDriver(options);
webDriver.manage().window().maximize();
this.webDriver.set(webDriver);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.yas.automation.ui.page;

import com.yas.automation.ui.hook.WebDriverFactory;
import lombok.Getter;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.function.Supplier;

/**
* BasePage provides common setup for web pages in the automation framework.
*
* <p>It includes:</p>
* <ul>
* <li>WebDriver setup.</li>
* <li>WebDriverWait setup.</li>
* <li>Common utility methods.</li>
* </ul>
*
* <p>Extend this class to use these features in your page classes.</p>
*/
@Getter
public class BasePage {

private final WebDriverFactory webDriverFactory;

public BasePage(WebDriverFactory webDriverFactory) {
this.webDriverFactory = webDriverFactory;
}

public void wait(Duration duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

public void scrollDown() {
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) getWebDriver();
javascriptExecutor.executeScript("window.scrollBy(0,document.body.scrollHeight)");
wait(Duration.ofSeconds(1));
}

public void scrollTo(WebElement webElement) {
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) getWebDriver();
javascriptExecutor.executeScript("arguments[0].scrollIntoView(true);", webElement);
wait(Duration.ofSeconds(1));
}

public boolean isElementPresent(Supplier<WebElement> webElementConsumer) {
try {
return webElementConsumer.get().isDisplayed();
} catch (Exception exception) {
return false;
}
}

public WebDriver getWebDriver() {
return webDriverFactory.getChromeDriver();
}

public WebDriverWait getWait() {
return new WebDriverWait(getWebDriver(), Duration.ofSeconds(30));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.yas.automation.ui.service;

import com.yas.automation.ui.form.InputType;
import org.openqa.selenium.WebElement;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class InputDelegateService {

private final Map<String, InputService> inputServiceMap;

public InputDelegateService(Map<String, InputService> inputServiceMap) {
this.inputServiceMap = inputServiceMap;
}

public void setInputValue(InputType inputType, WebElement webElement, Object value) {
if (inputServiceMap.containsKey(inputType.getServiceName())) {
inputServiceMap.get(inputType.getServiceName()).setValue(webElement, value);
} else {
throw new IllegalArgumentException("No input service found for: %s".formatted(inputType.getServiceName()));
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.yas.automation.ui.service;

import org.openqa.selenium.WebElement;

public interface InputService {

<T> void setValue(WebElement webElement, T value);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.yas.automation.ui.service.impl;

import com.yas.automation.ui.service.InputService;
import org.openqa.selenium.WebElement;
import org.springframework.stereotype.Component;

@Component
public class CheckBoxService implements InputService {
@Override
public void setValue(WebElement webElement, Object value) {
webElement.click();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.yas.automation.ui.service.impl;

import com.yas.automation.ui.service.InputService;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.springframework.stereotype.Component;

@Component
public class DropdownService implements InputService {

@Override
public void setValue(WebElement webElement, Object value) {
Select selectBrand = new Select(webElement);
selectBrand.selectByVisibleText((String) value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.yas.automation.ui.service.impl;

import com.yas.automation.ui.service.InputService;
import org.openqa.selenium.WebElement;
import org.springframework.stereotype.Component;

import java.nio.file.Paths;

@Component
public class FileService implements InputService {
@Override
public void setValue(WebElement webElement, Object value) {
webElement.sendKeys(Paths.get((String) value).toAbsolutePath().toString());
}
}
Loading

0 comments on commit 03cb7f7

Please sign in to comment.