├── .github └── workflows │ └── maven.yml ├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── nal │ ├── keywords │ ├── Browser.java │ └── KeywordExecutor.java │ ├── listeners │ ├── ExtentReportListener.java │ ├── FrameworkListener.java │ └── TestAllureListener.java │ └── utils │ └── CSVReader.java └── test ├── java └── tests │ └── KeywordDrivenTest.java └── resources ├── csvs ├── home_test.csv ├── login_test.csv └── search_test.csv └── testrunner └── testng.xml /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up JDK 11 and Maven 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '11' 20 | distribution: 'temurin' 21 | maven-version: '3.8.1' # Specify the desired Maven version 22 | cache: maven 23 | cache-dependency-path: 'sub-project/pom.xml' # optional 24 | - name: Build with Maven 25 | run: mvn clean install 26 | 27 | # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive 28 | - name: Update dependency graph 29 | uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | allure-results/ 2 | allure-report/ 3 | screenshots/ 4 | screenshot/ 5 | test-output/ 6 | build/ 7 | reports/ 8 | logs/ 9 | 10 | opencart.log 11 | ############################## 12 | ## Java 13 | ############################## 14 | .mtj.tmp/ 15 | *.class 16 | *.jar 17 | *.war 18 | *.ear 19 | *.nar 20 | hs_err_pid* 21 | activityLog.log 22 | ############################## 23 | ## Maven 24 | ############################## 25 | target/ 26 | pom.xml.tag 27 | pom.xml.releaseBackup 28 | pom.xml.versionsBackup 29 | pom.xml.next 30 | pom.xml.bak 31 | release.properties 32 | dependency-reduced-pom.xml 33 | buildNumber.properties 34 | .mvn/timing.properties 35 | .mvn/wrapper/maven-wrapper.jar 36 | 37 | ############################## 38 | ## IntelliJ 39 | ############################## 40 | out/ 41 | .idea/ 42 | .idea_modules/ 43 | *.iml 44 | *.ipr 45 | *.iws 46 | ############################## 47 | ## Eclipse 48 | ############################## 49 | .settings/ 50 | bin/ 51 | tmp/ 52 | .metadata 53 | .classpath 54 | .project 55 | *.tmp 56 | *.bak 57 | *.swp 58 | *~.nib 59 | local.properties 60 | .loadpath 61 | .factorypath 62 | 63 | ## OS X 64 | ############################## 65 | .DS_Store 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Screenshot 2024-03-04 at 8 02 29 PM# Keyword Driven Testing Framework with Selenium 2 | 3 | ## Overview 4 | This project demonstrates a Keyword Driven Testing (KDT) framework implemented using Selenium WebDriver in Java. The framework allows for easy creation and execution of automated test cases using a set of keywords defined in CSV files. 5 | 6 | ## Features 7 | - Keyword-driven approach for writing test cases. 8 | - Test cases defined in CSV files for easy maintenance and readability. 9 | - Support for parallel execution of test cases. 10 | - Integration with TestNG for test execution and reporting. 11 | - Flexible and extensible architecture. 12 | 13 | ## Project Structure 14 | - `src/main/java/`: Contains the source code for the Keyword Driven Testing framework. 15 | - `src/test/java/`: Contains the test scripts written using the framework. 16 | - `src/test/resources/csvs/`: Contains the CSV files defining the test cases in the form of keywords. 17 | - `testng.xml`: TestNG configuration file for executing the tests. 18 | 19 | ## Setup Instructions 20 | 1. Clone the repository to your local machine. 21 | 2. Import the project into your preferred Java IDE (e.g., Eclipse, IntelliJ IDEA). 22 | 3. Ensure that you have the necessary dependencies configured (e.g., Selenium WebDriver, TestNG). 23 | 4. Define your test cases in CSV files located in the `src/test/resources/csvs/` directory. 24 | 25 | ## Running Tests 26 | - You can run the tests using the TestNG XML configuration file (`testng.xml`). 27 | - Execute the `testng.xml` file using your IDE or the TestNG command-line interface. 28 | - Ensure that the WebDriver instance is properly initialized and managed during test execution. 29 | - Parallel run through thread-count and parallel tags in testng.xml 30 | 31 | ## Console Output 32 | - You can see the full csv file formatted data in the console output. 33 | Screenshot 2024-03-04 at 8 02 29 PM 34 | 35 | 36 | ## Test Reporting 37 | - TestNG generates detailed HTML reports after test execution. 38 | - The reports provide information about test results, including pass/fail status and error messages. 39 | - Integrated Extent and Allure reports as well, including pass/fail status, error messages and screenshot for failure tests. 40 | 41 | ## Information about reporting tools used in the project, such as Allure, ExtentReport. 42 | 43 | - Allure Report 44 | Allure is a flexible lightweight test report tool that not only shows a very concise representation of what have been tested in a neat web report form, but allows everyone participating in the development process to extract maximum of useful information from everyday execution of tests. 45 | 46 | - To generate Allure reports, follow these steps: 47 | 48 | - Install Allure command-line tool using the instructions provided in the Allure documentation. 49 | 50 | Execute your tests with Allure listeners attached. 51 | 52 | After the test execution is complete, generate the Allure report using the command: 53 | **allure generate --clean 54 | ** 55 | - View the generated report by running: 56 | **allure open 57 | ** 58 | Screenshot 2024-03-04 at 7 36 36 PM 59 | 60 | Screenshot 2024-03-04 at 7 36 59 PM 61 | 62 | Screenshot 2024-03-04 at 7 53 04 PM 63 | 64 | - Extent Report 65 | ExtentReports is an open-source reporting library for Java designed for the creation of beautiful, interactive, and detailed HTML reports. 66 | - After the test execution is complete, the Extent report will be generated automatically. 67 | - View the generated Extent report in the /reports directory. 68 | 69 | Screenshot 2024-03-04 at 7 38 08 PM 70 | 71 | Screenshot 2024-03-04 at 7 38 29 PM 72 | 73 | ## Contributing 74 | Contributions to improve the framework or add new features are welcome! If you find any issues or have suggestions for improvement, please open an issue or submit a pull request. 75 | 76 | ## License 77 | NA 78 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 4.0.0 7 | 8 | KeywordDriven 9 | KeywordDriven 10 | 0.0.1-SNAPSHOT 11 | 12 | KeywordDriven 13 | 14 | http://www.example.com 15 | 16 | 17 | UTF-8 18 | 11 19 | 11 20 | 5.0.8 21 | 1.9.19 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.seleniumhq.selenium 30 | selenium-java 31 | 4.17.0 32 | 33 | 34 | 35 | org.testng 36 | testng 37 | 7.0.0 38 | 39 | 40 | 41 | com.aventstack 42 | extentreports 43 | ${extentreports-version} 44 | 45 | 46 | 47 | io.qameta.allure 48 | allure-testng 49 | 2.23.0 50 | 51 | 52 | 53 | org.aspectj 54 | aspectjweaver 55 | ${aspectj.version} 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.8.1 65 | 66 | 11 67 | 11 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-surefire-plugin 75 | 2.20 76 | 77 | 78 | 3 79 | true 80 | 81 | src/test/resources/testrunner/testng.xml 82 | 83 | 84 | 85 | -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" 86 | 87 | 88 | 89 | 90 | 91 | org.aspectj 92 | aspectjweaver 93 | ${aspectj.version} 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/main/java/com/nal/keywords/Browser.java: -------------------------------------------------------------------------------- 1 | package com.nal.keywords; 2 | 3 | 4 | public enum Browser { 5 | chrome, 6 | firefox, 7 | safari, 8 | edge, 9 | ie 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/nal/keywords/KeywordExecutor.java: -------------------------------------------------------------------------------- 1 | package com.nal.keywords; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import org.openqa.selenium.By; 8 | import org.openqa.selenium.NoSuchElementException; 9 | import org.openqa.selenium.OutputType; 10 | import org.openqa.selenium.TakesScreenshot; 11 | import org.openqa.selenium.WebDriver; 12 | import org.openqa.selenium.chrome.ChromeDriver; 13 | import org.openqa.selenium.chrome.ChromeOptions; 14 | import org.openqa.selenium.edge.EdgeDriver; 15 | import org.openqa.selenium.firefox.FirefoxDriver; 16 | import org.openqa.selenium.firefox.FirefoxOptions; 17 | import org.openqa.selenium.ie.InternetExplorerDriver; 18 | import org.openqa.selenium.io.FileHandler; 19 | import org.openqa.selenium.safari.SafariDriver; 20 | import org.testng.Assert; 21 | 22 | import com.nal.utils.CSVReader; 23 | 24 | import io.qameta.allure.Step; 25 | 26 | /** 27 | * @author naveenautomationlabs This class contains methods to execute keywords 28 | * defined in test steps and perform corresponding actions. 29 | */ 30 | public class KeywordExecutor { 31 | 32 | /** 33 | * ThreadLocal instance to hold WebDriver instance for each thread. 34 | */ 35 | public static ThreadLocal tlDriver = new ThreadLocal(); 36 | 37 | /** 38 | * Executes the given keyword with the provided target and value. 39 | * 40 | * @param keyword The keyword to execute. 41 | * @param target The target element or location. 42 | * @param value The value to input or verify. 43 | */ 44 | @Step("{keyword} | {target} | {value} | ") 45 | public void executeKeyword(String keyword, String target, String value) { 46 | switch (keyword) { 47 | case "OpenBrowser": 48 | try { 49 | Browser browser = Browser.valueOf(target.toLowerCase()); 50 | openBrowser(browser); 51 | } catch (IllegalArgumentException e) { 52 | throw new IllegalArgumentException("Unsupported browser: " + value); 53 | } 54 | break; 55 | case "NavigateTo": 56 | navigateTo(target); 57 | break; 58 | case "Click": 59 | click(target); 60 | break; 61 | case "Type": 62 | type(target, value); 63 | break; 64 | case "VerifyText": 65 | verifyText(target, value); 66 | break; 67 | case "AssertElementPresent": 68 | assertElementPresent(target); 69 | break; 70 | case "SwitchToFrame": 71 | switchToFrame(target); 72 | break; 73 | case "SwitchToDefaultContent": 74 | switchToDefaultContent(); 75 | break; 76 | // Add more cases for other keyword interpretations 77 | default: 78 | throw new IllegalArgumentException("Unsupported keyword: " + keyword); 79 | } 80 | } 81 | 82 | /** 83 | * Executes the test steps for the given test case name. 84 | * 85 | * @param testCaseName The name of the test case. 86 | * @param testSteps The list of test steps to execute. 87 | */ 88 | public void executeTestSteps(String testCaseName, List testSteps) { 89 | System.out.println("Executing test case: " + testCaseName); 90 | for (String step : testSteps) { 91 | String[] parts = step.split(","); 92 | String keyword = parts[0].trim(); 93 | String target = parts[1].trim(); 94 | String value = ""; 95 | if (parts.length == 3) { 96 | value = parts[2].trim(); 97 | } 98 | executeKeyword(keyword, target, value); 99 | } 100 | } 101 | 102 | /** 103 | * Executes test cases from the given CSV file. 104 | * 105 | * @param csvFile The path to the CSV file containing test cases. 106 | */ 107 | public void executeTestCasesFromCSV(String csvFile) { 108 | CSVReader csvReader = new CSVReader(); 109 | csvReader.executeTestCasesFromCSV(csvFile); 110 | } 111 | 112 | /** 113 | * Asserts that the element specified by the target is present. 114 | * 115 | * @param target The target element. 116 | */ 117 | private void assertElementPresent(String target) { 118 | By locator = getBy(target); 119 | boolean isElementPresent = isElementPresent(locator); 120 | Assert.assertTrue(isElementPresent, "Element is not present: " + target); 121 | } 122 | 123 | /** 124 | * Checks if the element specified by the locator is present. 125 | * 126 | * @param locator The locator of the element. 127 | * @return True if the element is present, otherwise false. 128 | */ 129 | private boolean isElementPresent(By locator) { 130 | try { 131 | getDriver().findElement(locator); 132 | return true; 133 | } catch (NoSuchElementException e) { 134 | return false; 135 | } 136 | } 137 | 138 | /** 139 | * Opens the browser specified by the browserName. 140 | * 141 | * @param browser The browser to open. 142 | */ 143 | @Step("Open browser: {browser}") 144 | private void openBrowser(Browser browser) { 145 | WebDriver driver; 146 | System.out.println("==========browser : " + browser); 147 | switch (browser) { 148 | case chrome: 149 | ChromeOptions co = new ChromeOptions(); 150 | co.addArguments("--window-size=1920,1080"); 151 | co.addArguments("--no-sandbox"); 152 | co.addArguments("--headless"); 153 | co.addArguments("--disable-gpu"); 154 | co.addArguments("--disable-crash-reporter"); 155 | co.addArguments("--disable-extensions"); 156 | co.addArguments("--disable-in-process-stack-traces"); 157 | co.addArguments("--disable-logging"); 158 | co.addArguments("--disable-dev-shm-usage"); 159 | co.addArguments("--log-level=3"); 160 | co.addArguments("--output=/dev/null"); 161 | co.addArguments("ignore-certificate-errors"); 162 | driver = new ChromeDriver(co); 163 | break; 164 | case firefox: 165 | FirefoxOptions fo = new FirefoxOptions(); 166 | fo.addArguments("--window-size=1920,1080"); 167 | fo.addArguments("--no-sandbox"); 168 | fo.addArguments("--headless"); 169 | fo.addArguments("--disable-gpu"); 170 | fo.addArguments("--disable-crash-reporter"); 171 | fo.addArguments("--disable-extensions"); 172 | fo.addArguments("--disable-in-process-stack-traces"); 173 | fo.addArguments("--disable-logging"); 174 | fo.addArguments("--disable-dev-shm-usage"); 175 | fo.addArguments("--log-level=3"); 176 | fo.addArguments("--output=/dev/null"); 177 | fo.addArguments("ignore-certificate-errors"); 178 | driver = new FirefoxDriver(fo); 179 | break; 180 | case edge: 181 | driver = new EdgeDriver(); 182 | break; 183 | case ie: 184 | driver = new InternetExplorerDriver(); 185 | break; 186 | case safari: 187 | driver = new SafariDriver(); 188 | break; 189 | default: 190 | throw new IllegalArgumentException("Unsupported browser: " + browser); 191 | } 192 | tlDriver.set(driver); 193 | } 194 | 195 | /** 196 | * Navigates to the specified URL. 197 | * 198 | * @param url The URL to navigate to. 199 | */ 200 | @Step("Navigate to URL: {url}") 201 | private void navigateTo(String url) { 202 | getDriver().navigate().to(url); 203 | } 204 | 205 | /** 206 | * Clicks on the element specified by the target. 207 | * 208 | * @param target The target element to click. 209 | */ 210 | @Step("Click on element: {target}") 211 | private void click(String target) { 212 | By locator = getBy(target); 213 | getDriver().findElement(locator).click(); 214 | } 215 | 216 | /** 217 | * Types the specified value into the element specified by the target. 218 | * 219 | * @param target The target element to type into. 220 | * @param value The value to type. 221 | */ 222 | @Step("Type '{value}' into element: {target}") 223 | private void type(String target, String value) { 224 | By locator = getBy(target); 225 | getDriver().findElement(locator).sendKeys(value); 226 | } 227 | 228 | /** 229 | * Verifies that the text in the element specified by the target matches the 230 | * expectedText. 231 | * 232 | * @param target The target element containing the text to verify. 233 | * @param expectedText The expected text. 234 | */ 235 | @Step("Verify text '{expectedText}' in element: {target}") 236 | private void verifyText(String target, String expectedText) { 237 | By locator = getBy(target); 238 | String actualText = getDriver().findElement(locator).getText(); 239 | Assert.assertEquals(actualText, expectedText, "Text verification failed!"); 240 | } 241 | 242 | /** 243 | * Switches to the frame specified by the frameName. 244 | * 245 | * @param frameName The name or ID of the frame to switch to. 246 | */ 247 | @Step("Switch to frame: {frameName}") 248 | private void switchToFrame(String frameName) { 249 | getDriver().switchTo().frame(frameName); 250 | } 251 | 252 | /** 253 | * Switches to the default content. 254 | */ 255 | @Step("Switch to default content") 256 | private void switchToDefaultContent() { 257 | getDriver().switchTo().defaultContent(); 258 | } 259 | 260 | /** 261 | * Retrieves the By locator for the specified target. 262 | * 263 | * @param target The target element. 264 | * @return The By locator for the target. 265 | */ 266 | private By getBy(String target) { 267 | By locator; 268 | if (target.startsWith("id=")) { 269 | locator = By.id(target.substring(3)); 270 | } else if (target.startsWith("name=")) { 271 | locator = By.name(target.substring(5)); 272 | } else if (target.startsWith("class=")) { 273 | locator = By.className(target.substring(6)); 274 | } else if (target.startsWith("xpath=")) { 275 | locator = By.xpath(target.substring(6)); 276 | } else if (target.startsWith("css=")) { 277 | locator = By.cssSelector(target.substring(4)); 278 | } else if (target.startsWith("linktext=")) { 279 | locator = By.linkText(target.substring(9)); 280 | } else { 281 | throw new IllegalArgumentException("Unsupported locator format: " + target); 282 | } 283 | return locator; 284 | } 285 | 286 | /** 287 | * Retrieves the WebDriver instance. 288 | * 289 | * @return The WebDriver instance. 290 | */ 291 | public static WebDriver getDriver() { 292 | return tlDriver.get(); 293 | } 294 | 295 | /** 296 | * Takes a screenshot of the current WebDriver instance and saves it to the 297 | * specified file. 298 | * 299 | * @param methodName The name of the method or test. 300 | * @return The path to the saved screenshot file. 301 | */ 302 | public static String getScreenshot(String methodName) { 303 | File srcFile = ((TakesScreenshot) getDriver()).getScreenshotAs(OutputType.FILE); 304 | String path = System.getProperty("user.dir") + "/screenshot/" + methodName + "_" + System.currentTimeMillis() 305 | + ".png"; 306 | File destination = new File(path); 307 | try { 308 | FileHandler.copy(srcFile, destination); 309 | } catch (IOException e) { 310 | e.printStackTrace(); 311 | } 312 | return path; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/main/java/com/nal/listeners/ExtentReportListener.java: -------------------------------------------------------------------------------- 1 | package com.nal.listeners; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | 10 | import org.testng.ITestContext; 11 | import org.testng.ITestListener; 12 | import org.testng.ITestResult; 13 | 14 | import com.aventstack.extentreports.ExtentReports; 15 | import com.aventstack.extentreports.ExtentTest; 16 | import com.aventstack.extentreports.MediaEntityBuilder; 17 | import com.aventstack.extentreports.reporter.ExtentSparkReporter; 18 | import com.nal.keywords.KeywordExecutor; 19 | 20 | 21 | public class ExtentReportListener implements ITestListener { 22 | 23 | private static final String OUTPUT_FOLDER = "./reports/"; 24 | private static final String FILE_NAME = "TestExecutionReport.html"; 25 | 26 | private static ExtentReports extent = init(); 27 | public static ThreadLocal test = new ThreadLocal(); 28 | private static ExtentReports extentReports; 29 | 30 | 31 | private static ExtentReports init() { 32 | 33 | Path path = Paths.get(OUTPUT_FOLDER); 34 | // if directory exists? 35 | if (!Files.exists(path)) { 36 | try { 37 | Files.createDirectories(path); 38 | } catch (IOException e) { 39 | // fail to create directory 40 | e.printStackTrace(); 41 | } 42 | } 43 | 44 | extentReports = new ExtentReports(); 45 | ExtentSparkReporter reporter = new ExtentSparkReporter(OUTPUT_FOLDER + FILE_NAME); 46 | reporter.config().setReportName("Open Cart Automation Test Results"); 47 | extentReports.attachReporter(reporter); 48 | extentReports.setSystemInfo("System", "MAC"); 49 | extentReports.setSystemInfo("Author", "Naveen AutomationLabs"); 50 | extentReports.setSystemInfo("Build#", "1.1"); 51 | extentReports.setSystemInfo("Team", "OpenCart QA Team"); 52 | extentReports.setSystemInfo("Customer Name", "NAL"); 53 | extentReports.setSystemInfo("ENV NAME", System.getProperty("env")); 54 | 55 | return extentReports; 56 | } 57 | 58 | @Override 59 | public synchronized void onStart(ITestContext context) { 60 | System.out.println("Test Suite started!"); 61 | 62 | } 63 | 64 | @Override 65 | public synchronized void onFinish(ITestContext context) { 66 | System.out.println(("Test Suite is ending!")); 67 | extent.flush(); 68 | test.remove(); 69 | } 70 | 71 | @Override 72 | public synchronized void onTestStart(ITestResult result) { 73 | String methodName = result.getMethod().getMethodName(); 74 | String qualifiedName = result.getMethod().getQualifiedName(); 75 | int last = qualifiedName.lastIndexOf("."); 76 | int mid = qualifiedName.substring(0, last).lastIndexOf("."); 77 | String className = qualifiedName.substring(mid + 1, last); 78 | 79 | System.out.println(methodName + " started!"); 80 | ExtentTest extentTest = extent.createTest(result.getMethod().getMethodName(), 81 | result.getMethod().getDescription()); 82 | 83 | extentTest.assignCategory(result.getTestContext().getSuite().getName()); 84 | /* 85 | * methodName = StringUtils.capitalize(StringUtils.join(StringUtils. 86 | * splitByCharacterTypeCamelCase(methodName), StringUtils.SPACE)); 87 | */ 88 | extentTest.assignCategory(className); 89 | test.set(extentTest); 90 | test.get().getModel().setStartTime(getTime(result.getStartMillis())); 91 | } 92 | 93 | public synchronized void onTestSuccess(ITestResult result) { 94 | String methodName = result.getMethod().getMethodName(); 95 | System.out.println((methodName + " passed!")); 96 | test.get().pass("Test passed"); 97 | //test.get().pass(result.getThrowable(), MediaEntityBuilder.createScreenCaptureFromPath(DriverFactory.getScreenshot(methodName), methodName).build()); 98 | test.get().getModel().setEndTime(getTime(result.getEndMillis())); 99 | } 100 | 101 | public synchronized void onTestFailure(ITestResult result) { 102 | System.out.println((result.getMethod().getMethodName() + " failed!")); 103 | String methodName = result.getMethod().getMethodName(); 104 | test.get().fail("Test failed"); 105 | test.get().fail(result.getThrowable(), MediaEntityBuilder.createScreenCaptureFromPath(KeywordExecutor.getScreenshot(methodName), methodName).build()); 106 | test.get().getModel().setEndTime(getTime(result.getEndMillis())); 107 | } 108 | 109 | public synchronized void onTestSkipped(ITestResult result) { 110 | System.out.println((result.getMethod().getMethodName() + " skipped!")); 111 | //String methodName = result.getMethod().getMethodName(); 112 | 113 | test.get().skip("Test skipped"); 114 | 115 | //test.get().skip(result.getThrowable(), MediaEntityBuilder.createScreenCaptureFromPath(DriverFactory.getScreenshot(methodName), methodName).build()); 116 | test.get().getModel().setEndTime(getTime(result.getEndMillis())); 117 | } 118 | 119 | public synchronized void onTestFailedButWithinSuccessPercentage(ITestResult result) { 120 | System.out.println(("onTestFailedButWithinSuccessPercentage for " + result.getMethod().getMethodName())); 121 | } 122 | 123 | private Date getTime(long millis) { 124 | Calendar calendar = Calendar.getInstance(); 125 | calendar.setTimeInMillis(millis); 126 | return calendar.getTime(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/nal/listeners/FrameworkListener.java: -------------------------------------------------------------------------------- 1 | package com.nal.listeners; 2 | 3 | import org.testng.ITestContext; 4 | import org.testng.ITestListener; 5 | import org.testng.ITestResult; 6 | 7 | //custom listener 8 | public class FrameworkListener implements ITestListener{ 9 | 10 | 11 | @Override 12 | public synchronized void onStart(ITestContext context) { 13 | System.out.println("Test Suite started!"); 14 | 15 | } 16 | 17 | @Override 18 | public synchronized void onFinish(ITestContext context) { 19 | System.out.println(("Test Suite is ending!")); 20 | 21 | } 22 | 23 | @Override 24 | public synchronized void onTestStart(ITestResult result) { 25 | String methodName = result.getMethod().getMethodName(); 26 | String qualifiedName = result.getMethod().getQualifiedName(); 27 | int last = qualifiedName.lastIndexOf("."); 28 | int mid = qualifiedName.substring(0, last).lastIndexOf("."); 29 | String className = qualifiedName.substring(mid + 1, last); 30 | 31 | System.out.println("test method started : " + className + " : " + methodName); 32 | 33 | } 34 | 35 | public synchronized void onTestSuccess(ITestResult result) { 36 | String methodName = result.getMethod().getMethodName(); 37 | System.out.println((methodName + " passed!")); 38 | 39 | } 40 | 41 | public synchronized void onTestFailure(ITestResult result) { 42 | String methodName = result.getMethod().getMethodName(); 43 | System.out.println(methodName + " failed!"); 44 | 45 | } 46 | 47 | public synchronized void onTestSkipped(ITestResult result) { 48 | String methodName = result.getMethod().getMethodName(); 49 | System.out.println(methodName + " skipped!"); 50 | } 51 | 52 | 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/nal/listeners/TestAllureListener.java: -------------------------------------------------------------------------------- 1 | package com.nal.listeners; 2 | 3 | 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | 9 | import org.openqa.selenium.OutputType; 10 | import org.openqa.selenium.TakesScreenshot; 11 | import org.openqa.selenium.WebDriver; 12 | import org.testng.ITestContext; 13 | import org.testng.ITestListener; 14 | import org.testng.ITestResult; 15 | 16 | import com.nal.keywords.KeywordExecutor; 17 | 18 | import io.qameta.allure.Attachment; 19 | 20 | 21 | public class TestAllureListener implements ITestListener { 22 | 23 | 24 | 25 | private static String getTestMethodName(ITestResult iTestResult) { 26 | return iTestResult.getMethod().getConstructorOrMethod().getName(); 27 | } 28 | 29 | 30 | // Text attachments for Allure 31 | @Attachment(value = "Page screenshot", type = "image/png") 32 | public byte[] saveScreenshotPNG(WebDriver driver) { 33 | return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES); 34 | } 35 | 36 | // Text attachments for Allure 37 | @Attachment(value = "{0}", type = "text/plain") 38 | public static String saveTextLog(String message) { 39 | return message; 40 | } 41 | 42 | // HTML attachments for Allure 43 | @Attachment(value = "{0}", type = "text/html") 44 | public static String attachHtml(String html) { 45 | return html; 46 | } 47 | 48 | // Attaching CSV file content as text/plain attachment 49 | // Attaching CSV file as a text/csv attachment 50 | @Attachment(value = "CSV File", type = "text/csv") 51 | public byte[] attachCSVFile(String filePath) { 52 | try { 53 | Path path = Paths.get(filePath); 54 | return Files.readAllBytes(path); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | return null; 58 | } 59 | } 60 | 61 | @Override 62 | public void onStart(ITestContext iTestContext) { 63 | System.out.println("I am in onStart method " + iTestContext.getName()); 64 | } 65 | 66 | @Override 67 | public void onFinish(ITestContext iTestContext) { 68 | System.out.println("I am in onFinish method " + iTestContext.getName()); 69 | //attachCSVFile(TestExecutor.csvPath); 70 | 71 | } 72 | 73 | @Override 74 | public void onTestStart(ITestResult iTestResult) { 75 | System.out.println("I am in onTestStart method " + getTestMethodName(iTestResult) + " start"); 76 | } 77 | 78 | @Override 79 | public void onTestSuccess(ITestResult iTestResult) { 80 | System.out.println("I am in onTestSuccess method " + getTestMethodName(iTestResult) + " succeed"); 81 | } 82 | 83 | @Override 84 | public void onTestFailure(ITestResult iTestResult) { 85 | System.out.println("I am in onTestFailure method " + getTestMethodName(iTestResult) + " failed"); 86 | //Object testClass = iTestResult.getInstance(); 87 | //WebDriver driver = BasePage.getDriver(); 88 | // Allure ScreenShotRobot and SaveTestLog 89 | if (KeywordExecutor.getDriver() instanceof WebDriver) { 90 | System.out.println("Screenshot captured for test case:" + getTestMethodName(iTestResult)); 91 | saveScreenshotPNG(KeywordExecutor.getDriver()); 92 | } 93 | // Save a log on allure. 94 | saveTextLog(getTestMethodName(iTestResult) + " failed and screenshot taken!"); 95 | } 96 | 97 | @Override 98 | public void onTestSkipped(ITestResult iTestResult) { 99 | System.out.println("I am in onTestSkipped method " + getTestMethodName(iTestResult) + " skipped"); 100 | } 101 | 102 | @Override 103 | public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) { 104 | System.out.println("Test failed but it is in defined success ratio " + getTestMethodName(iTestResult)); 105 | } 106 | 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/com/nal/utils/CSVReader.java: -------------------------------------------------------------------------------- 1 | package com.nal.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import com.nal.keywords.KeywordExecutor; 10 | 11 | /** 12 | * @author naveenautomationlabs 13 | * This class reads test cases from CSV files and executes them using a KeywordExecutor. 14 | */ 15 | public class CSVReader { 16 | 17 | private static final Object lock = new Object(); 18 | 19 | /** 20 | * Path to the CSV file being processed. 21 | */ 22 | public static String csvPath = null; 23 | 24 | /** 25 | * Reads and executes test cases from a CSV file. 26 | * 27 | * @param csvFile The path to the CSV file containing test cases. 28 | */ 29 | public void executeTestCasesFromCSV(String csvFile) { 30 | csvPath = "./src/test/resources/csvs/" + csvFile; 31 | printTestCasesFromCSV(csvFile); 32 | try (BufferedReader br = new BufferedReader( 33 | new FileReader("./src/test/resources/csvs/" + csvFile))) { 34 | String line; 35 | String currentTestCase = null; 36 | List testSteps = new ArrayList<>(); 37 | 38 | while ((line = br.readLine()) != null) { 39 | String[] data = line.split(","); 40 | String testCaseName = data[0]; 41 | String keyword = data[1].trim(); 42 | String target = data[2].trim(); 43 | String value = (data.length == 4) ? data[3].trim() : ""; 44 | 45 | if (!testCaseName.equals(currentTestCase)) { 46 | // Execute previous test case steps 47 | if (currentTestCase != null) { 48 | KeywordExecutor keywordExecutor = new KeywordExecutor(); 49 | keywordExecutor.executeTestSteps(currentTestCase, testSteps); 50 | testSteps.clear(); 51 | } 52 | currentTestCase = testCaseName; 53 | } 54 | 55 | // Add current test step to the list 56 | testSteps.add(keyword + "," + target + "," + value); 57 | } 58 | 59 | // Execute the last test case 60 | if (currentTestCase != null) { 61 | KeywordExecutor keywordExecutor = new KeywordExecutor(); 62 | keywordExecutor.executeTestSteps(currentTestCase, testSteps); 63 | } 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | 69 | /** 70 | * Prints the test cases from the given CSV file. 71 | * 72 | * @param csvFile The path to the CSV file containing test cases. 73 | */ 74 | private void printTestCasesFromCSV(String csvFile) { 75 | synchronized (lock) { 76 | try (BufferedReader br = new BufferedReader( 77 | new FileReader("./src/test/resources/csvs/" + csvFile))) { 78 | String line; 79 | System.out.println("Test cases from CSV file: " + csvFile); 80 | System.out.println( 81 | "+-------------------------------------------------------------------------------------------+"); 82 | System.out.printf("| %-15s | %-15s | %-50s | %-20s |\n", "Test Case", "Keyword", "Target", "Value"); 83 | System.out.println( 84 | "+-------------------------------------------------------------------------------------------+"); 85 | while ((line = br.readLine()) != null) { 86 | String[] data = line.split(","); 87 | String testCase = data[0]; 88 | String keyword = data[1]; 89 | String target = data[2]; 90 | String value = ""; 91 | if (data.length > 3) { 92 | value = data[3]; 93 | } 94 | System.out.printf("| %-15s | %-15s | %-50s | %-20s |\n", testCase, keyword, target, value); 95 | } 96 | System.out.println("+----------------------------------------------------------------+"); 97 | System.out.println("Total test cases: " + getTestCaseCount(csvFile)); 98 | System.out.println("+----------------------------------------------------------------+"); 99 | System.out.println(); 100 | System.out.println(); 101 | } catch (IOException e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Counts the number of test cases in the given CSV file. 109 | * 110 | * @param csvFile The path to the CSV file containing test cases. 111 | * @return The number of test cases in the CSV file. 112 | * @throws IOException If an I/O error occurs. 113 | */ 114 | private int getTestCaseCount(String csvFile) throws IOException { 115 | int count = 0; 116 | try (BufferedReader br = new BufferedReader( 117 | new FileReader("./src/test/resources/csvs/" + csvFile))) { 118 | while (br.readLine() != null) { 119 | count++; 120 | } 121 | } 122 | return count; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/test/java/tests/KeywordDrivenTest.java: -------------------------------------------------------------------------------- 1 | 2 | package tests; 3 | 4 | import org.testng.annotations.AfterMethod; 5 | import org.testng.annotations.Listeners; 6 | import org.testng.annotations.Test; 7 | 8 | import com.nal.keywords.KeywordExecutor; 9 | import com.nal.listeners.TestAllureListener; 10 | 11 | @Listeners(TestAllureListener.class) 12 | public class KeywordDrivenTest { 13 | 14 | @AfterMethod 15 | public void tearDown() { 16 | if (KeywordExecutor.getDriver() != null) { 17 | KeywordExecutor.getDriver().close(); 18 | } 19 | } 20 | 21 | @Test 22 | public void executeLoginTest() { 23 | KeywordExecutor keywordExecutor = new KeywordExecutor(); 24 | keywordExecutor.executeTestCasesFromCSV("login_test.csv"); 25 | } 26 | 27 | @Test 28 | public void executeSearchTest() { 29 | KeywordExecutor keywordExecutor = new KeywordExecutor(); 30 | keywordExecutor.executeTestCasesFromCSV("search_test.csv"); 31 | } 32 | 33 | @Test 34 | public void executeHomeTest() { 35 | KeywordExecutor keywordExecutor = new KeywordExecutor(); 36 | keywordExecutor.executeTestCasesFromCSV("home_test.csv"); 37 | } 38 | } -------------------------------------------------------------------------------- /src/test/resources/csvs/home_test.csv: -------------------------------------------------------------------------------- 1 | Home, OpenBrowser, chrome, 2 | Home, NavigateTo, https://naveenautomationlabs.com/opencart/index.php?route=account/account, 3 | Home, Type, id=input-email, pwtest@opencart.com 4 | Home, Type, id=input-password, pw123 5 | Home, Click, xpath=//input[@value='Login'], 6 | Home, VerifyText, xpath=//h2[normalize-space()='My Account'], My Account -------------------------------------------------------------------------------- /src/test/resources/csvs/login_test.csv: -------------------------------------------------------------------------------- 1 | Login, OpenBrowser, chrome, 2 | Login, NavigateTo, https://naveenautomationlabs.com/opencart/index.php?route=account/account, 3 | Login, Type, id=input-email, pwtest@opencart.com 4 | Login, Type, id=input-password, pw123 5 | Login, Click, xpath=//input[@value='Login'], 6 | Login, VerifyText, xpath=//h2[normalize-space()='My Account'], My Account -------------------------------------------------------------------------------- /src/test/resources/csvs/search_test.csv: -------------------------------------------------------------------------------- 1 | Search, OpenBrowser, firefox, 2 | Search, NavigateTo, https://naveenautomationlabs.com/opencart/index.php?route=account/account, 3 | Search, Type, id=input-email, pwtest@opencart.com 4 | Search, Type, id=input-password, pw123 5 | Search, Click, xpath=//input[@value='Login'], 6 | Search,Type,name=search,macbook 7 | Search,Click,css=button[class='btn btn-default btn-lg'], -------------------------------------------------------------------------------- /src/test/resources/testrunner/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------