Skip to content

Commit

Permalink
HPCC-31857 : Develop an automated ECL Watch Test Suite
Browse files Browse the repository at this point in the history
Developed a testing framework using Java, Selenium, and TestNG. The framework is initiated by `TestRunner.java`, which loads all test classes listed in `config/TestClasses.java` and executes them sequentially. Each test class contains at least one method annotated with `@Test`, which serves as the entry point for the tests. Test cases cover the Activities and ECL Workunit pages, including tests for text presence, link functionality, sorting order, workunits content, description, and checkbox functionality. Added loggers in separate files for error, debug, and detail. Updated YML files to reflect jars and commands for the current testing framework. Additionally, provided comprehensive documentation with UML diagrams and detailed explanations of each method within the classes.

Signed-off-by: Nisha Bagdwal <[email protected]>
  • Loading branch information
Nisha-Bagdwal committed Jul 15, 2024
1 parent 309ed54 commit 8c5d558
Show file tree
Hide file tree
Showing 29 changed files with 1,950 additions and 618 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@ jobs:
${{ github.workspace }}/HPCC-Platform/.github/workflows/timeoutcmd
if-no-files-found: error

- name: Upload UI Test Files
- name: Upload ECL Watch UI Test Files
if: ${{ inputs.upload-package == true }}
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.asset-name }}-ui_test-files
name: ${{ inputs.asset-name }}-ecl_watch_ui_tests
path: |
${{ github.workspace }}/HPCC-Platform/esp/src/test-ui/**/*
${{ github.workspace }}/HPCC-Platform/esp/src/test-ui/tests/**/*
if-no-files-found: error

- name: Upload Error Logs
Expand Down
55 changes: 31 additions & 24 deletions .github/workflows/test-ui-gh_runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ jobs:
- name: Download UI Test Files
uses: actions/download-artifact@v3
with:
name: ${{ inputs.asset-name }}-ui_test-files
path: ${{ inputs.asset-name }}-ui_test-files
name: ${{ inputs.asset-name }}-ecl_watch_ui_tests
path: ${{ inputs.asset-name }}-ecl_watch_ui_tests

- name: Check ECLWatch UI Test Directory
id: check
run: |
if [[ ! -d ${{ inputs.asset-name }}-ui_test-files ]]
if [[ ! -d ${{ inputs.asset-name }}-ecl_watch_ui_tests ]]
then
echo "ECLWatch UI ${{ inputs.asset-name }}-ui_test-files directory missing."
echo "ECLWatch UI ${{ inputs.asset-name }}-ecl_watch_ui_tests directory missing."
else
javaFilesCount=$(find ${{ inputs.asset-name }}-ui_test-files/ -iname '*.java' -type f -print | wc -l )
javaFilesCount=$(find ${{ inputs.asset-name }}-ecl_watch_ui_tests/ -iname '*.java' -type f -print | wc -l )
echo "Number of test java files is $javaFilesCount"
if [[ ${javaFilesCount} -eq 0 ]]
then
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
chmod +x ./${{ inputs.asset-name }}-support-files/*
sudo cp ./${{ inputs.asset-name }}-support-files/* /opt/HPCCSystems/bin
chmod +x ./${{ inputs.asset-name }}-ui_test-files/*
chmod +x ./${{ inputs.asset-name }}-ecl_watch_ui_tests/*
- name: Start HPCC-Platform
shell: "bash"
Expand Down Expand Up @@ -136,34 +136,41 @@ jobs:
run: |
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt-get install -y ./google-chrome-stable_current_amd64.deb
wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip
unzip chromedriver_linux64.zip
sudo mv chromedriver /usr/bin/chromedriver
wget https://storage.googleapis.com/chrome-for-testing-public/126.0.6478.126/linux64/chromedriver-linux64.zip
unzip chromedriver-linux64.zip -d chromedriver
sudo mv chromedriver/chromedriver-linux64/chromedriver /usr/bin/chromedriver
sudo chown root:root /usr/bin/chromedriver
sudo chmod +x /usr/bin/chromedriver
wget https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar
wget http://www.java2s.com/Code/JarDownload/testng/testng-6.8.7.jar.zip
unzip testng-6.8.7.jar.zip
- name: Run Tests
timeout-minutes: 10 # generous, current runtime is ~1min, this should be increased if new tests are added
wget https://repo1.maven.org/maven2/org/testng/testng/7.7.1/testng-7.7.1.jar
wget https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.17.0/jackson-annotations-2.17.0.jar
wget https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.17.0/jackson-core-2.17.0.jar
wget https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.17.0/jackson-databind-2.17.0.jar
wget https://repo1.maven.org/maven2/com/beust/jcommander/1.82/jcommander-1.82.jar
wget https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.22.0/selenium-java-4.22.0.zip
wget https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar
wget https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.7.30/slf4j-simple-1.7.30.jar
unzip selenium-java-4.22.0.zip -d selenium-libs
- name: Run ECL Watch UI Tests
timeout-minutes: 80 # generous, current runtime is ~38 minutes, this should be increased if new tests are added
if: steps.check.outputs.runtests
shell: "bash"
run: |
export CLASSPATH=".:${{ github.workspace }}/selenium-server-standalone-3.141.59.jar:${{ github.workspace }}/testng-6.8.7.jar"
pushd ${{ inputs.asset-name }}-ui_test-files
./run.sh tests http://localhost:8010 > eclWatchUiTest.log 2>&1
retCode=$?
echo "UI test done"
[[ $retCode -ne 0 ]] && exit 1
export CLASSPATH=".:${{ inputs.asset-name }}-ecl_watch_ui_tests:${{ github.workspace }}/selenium-libs/*:${{ github.workspace }}/testng-7.7.1.jar:${{ github.workspace }}/jackson-annotations-2.17.0.jar:${{ github.workspace }}/jackson-core-2.17.0.jar:${{ github.workspace }}/jackson-databind-2.17.0.jar:${{ github.workspace }}/jcommander-1.82.jar:${{ github.workspace }}/slf4j-api-1.7.30.jar:${{ github.workspace }}/slf4j-simple-1.7.30.jar"
pushd ${{ inputs.asset-name }}-ecl_watch_ui_tests
find . -iname '*.java' -type f -print -exec javac {} \;
java framework.TestRunner detail
echo "ECL Watch UI test done"
lines=$(wc -l < error_ecl_test.log)
[[ $lines -ne 0 ]] && exit 1
popd
- name: eclwatch-ui-test-logs-artifact
- name: Upload ECL Watch UI Test Logs To Artifact
if: ${{ failure() || cancelled() }}
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.asset-name }}-ui_test-logs
name: ${{ inputs.asset-name }}-ecl_watch_ui_test_logs
path: |
${{ inputs.asset-name }}-ui_test-files/eclWatchUiTest.log
${{ inputs.asset-name }}-ecl_watch_ui_tests/*.log
/home/runner/HPCCSystems-regression/log/*.json
if-no-files-found: error
2 changes: 1 addition & 1 deletion esp/src/test-ui/tests/Activities.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static void main(String[] args) throws IOException, InterruptedException
Capabilities caps = ((RemoteWebDriver) driver).getCapabilities();

String browserName = caps.getBrowserName();
String browserVersion = caps.getVersion();
//String browserVersion = caps.getVersion();
// System.out.println(browserName+" "+browserVersion);

driver.get(args[0]);
Expand Down
42 changes: 40 additions & 2 deletions esp/src/test-ui/tests/framework/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Project: An Automated ECL Watch Test Suite
### Project: An Automated ECL Watch Test Suite

This project's code begins with the TestRunner.java file. The main method in this class loads all the Java classes
created for writing test cases for specific web pages of the ECL Watch UI and then runs the tests in those classes
Expand All @@ -8,4 +8,42 @@ The names of the Java classes that the TestRunner class needs to load should be
file.

Each Java class created to write tests for specific web pages should have at least one method annotated with @Test. The
code for each class starts to run from this method.
code for each class starts to run from this method.

#### Important Note: ChromeDriver Version Compatibility

If the Chrome browser version updates in the future, it's crucial to ensure that the corresponding ChromeDriver version is also updated. Failure to do so may cause tests to fail due to compatibility issues between the browser and driver. Always verify and update ChromeDriver to the latest version whenever running tests to maintain compatibility and ensure smooth test execution.

#### CLI Arguments for TestRunner.java

While running the test suite, you can pass arguments in this way -> "-l log_level -p path".
- "log_level" is of two types "debug" and "detail"
- "debug" means generate error log file with a debug log file.
- "detail" means generate error log file with a detailed debug file.
- If no -l and log_level is passed in the argument, only error log will be generated
- "path" is the path of the folder where the json files are
- The code will log an error if the '-p' and 'path' arguments are not provided, as the JSON folder path is required for the test suite.

path could be something like:

for GitHub Actions -> /home/runner/HPCCSystems-regression/log/

for local machine -> C:/Users/nisha/Documents/Internship/Work/files/

So an example of complete CLI arguments would look like this:

-l debug -p C:/Users/nisha/Documents/Internship/Work/files/

Below are the dependencies used in the project:

- https://repo1.maven.org/maven2/org/testng/testng/7.7.1/testng-7.7.1.jar
- https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-annotations/2.17.0/jackson-annotations-2.17.0.jar
- https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/2.17.0/jackson-core-2.17.0.jar
- https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.17.0/jackson-databind-2.17.0.jar
- https://repo1.maven.org/maven2/com/beust/jcommander/1.82/jcommander-1.82.jar
- https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.17.0/selenium-server-4.17.0.jar

Notes:
1. Users need to run these tests with regression test suite only.
2. Code should be updated accordingly if selenium server jar updates.
3. For future testing developers, custom class names or attributes defined by UI developers can change frequently during updates or redesigns. However, standard attributes that are part of the HTML specifications (such as id, type, value, href, aria-sort, aria-disabled, etc.) are much more stable. Therefore, it is advisable to use only standard HTML attributes to access web elements. This approach ensures that test cases remain consistent and are less likely to break due to UI changes.
106 changes: 43 additions & 63 deletions esp/src/test-ui/tests/framework/TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,66 +3,63 @@
import framework.config.Config;
import framework.config.TestClasses;
import framework.model.TestClass;
import framework.setup.TestInjector;
import framework.utility.Common;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.TestNG;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;


public class TestRunner {
public static void main(String[] args) {

Logger logger = setupLogger();
WebDriver driver = setupWebDriver();
try {
getLogLevelAndJSONFolderPath(args);
Common.initializeLoggerAndDriver();

if (Common.driver != null) {
TestNG testng = new TestNG();
testng.setTestClasses(loadClasses());
testng.run();
Common.driver.quit();
}

TestNG testng = new TestNG();
testng.setTestClasses(loadClasses());
testng.addListener(new TestInjector(logger, driver));
testng.run();
driver.quit();
} catch (Exception e) {
Common.logError("Exception occurred in TestRunner.java: " + e.getMessage());
}
}

private static WebDriver setupWebDriver() {

ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--headless"); // sets the ChromeDriver to run in headless mode, meaning it runs without opening a visible browser window.
chromeOptions.addArguments("--no-sandbox"); // disables the sandbox security feature in Chrome.
chromeOptions.addArguments("--log-level=3"); // sets the log level for the ChromeDriver. Level 3 corresponds to errors only.

System.setProperty("webdriver.chrome.silentOutput", "true"); // suppresses the logs generated by the ChromeDriver

WebDriver driver;

if (Common.isRunningOnLocal()) {
System.setProperty("webdriver.chrome.driver", Config.PATH_LOCAL_CHROME_DRIVER); // sets the system property to the path of the ChromeDriver executable.
try {
driver = new RemoteWebDriver(URI.create(Config.LOCAL_SELENIUM_SERVER).toURL(), chromeOptions);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
// Parses the command-line arguments to set the log level and JSON folder path.
// The method checks for the presence of '-l' (log_level) and '-p' (path) arguments.
// Logs an error if the '-p' argument is not provided, as the JSON path is required.
// path is the path of the folder where the json files are
// log level is of two types "debug" and "detail"
// "debug" means generate error log file with a debug log file.
// "detail" means generate error log file with a detailed debug file.
// if no -l and log level is passed in the argument, only error log will be generated

public static void getLogLevelAndJSONFolderPath(String[] args) { // -l <log_level> -p <path>

String log_level = null;
String path = null;

for (int i = 0; i < args.length; i++) {
if ("-l".equals(args[i]) && i + 1 < args.length) {
log_level = args[++i];
} else if ("-p".equals(args[i]) && i + 1 < args.length) {
path = args[++i];
}
} else {
System.setProperty("webdriver.chrome.driver", Config.PATH_GH_ACTION_CHROME_DRIVER);
driver = new ChromeDriver(chromeOptions);
}

//Capabilities caps = ((RemoteWebDriver) driver).getCapabilities();
//String browserName = caps.getBrowserName();
//String browserVersion = caps.getBrowserVersion();
//System.out.println(browserName+" "+browserVersion);
if (log_level != null) {
Config.LOG_LEVEL = log_level;
}

return driver;
if (path != null) {
Config.PATH_FOLDER_JSON = path;
} else {
Common.logError("Error: JSON folder path is required. Use -p <path> to specify the path.");
}
}

private static Class<?>[] loadClasses() {
Expand All @@ -71,28 +68,11 @@ private static Class<?>[] loadClasses() {
for (TestClass testClass : TestClasses.testClassesList) {
try {
classes.add(Class.forName(testClass.getPath()));
} catch (ClassNotFoundException e) {
System.err.println(e.getMessage());
} catch (Exception e) {
Common.logError("Failure: Error in loading classes: " + e.getMessage());
}
}

return classes.toArray(new Class<?>[0]);
}

private static Logger setupLogger() {
Logger logger = Logger.getLogger(TestRunner.class.getName());
try {
FileHandler fileHandler = new FileHandler(Config.LOG_FILE);
SimpleFormatter formatter = new SimpleFormatter();
fileHandler.setFormatter(formatter);
logger.addHandler(fileHandler);
logger.setUseParentHandlers(false);
logger.setLevel(Level.ALL);
} catch (IOException e) {
System.err.println("Failed to setup logger: " + e.getMessage());
}

Logger.getLogger("org.openqa.selenium").setLevel(Level.OFF); // turn off all logging from the Selenium WebDriver.
return logger;
}
}
29 changes: 19 additions & 10 deletions esp/src/test-ui/tests/framework/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@

public class Config {

public static final String LOG_FILE = "errorLog.log";
public static final String LOG_FILE_ERROR = "error_ecl_test.log";
public static final String LOG_FILE_DEBUG = "debug_ecl_test.log";
public static final String LOG_FILE_DETAIL = "detail_ecl_test.log";
public static final String LOCAL_OS = "Windows";
public static final String LOCAL_USER_PROFILE = "C:\\Users\\nisha";
public static final String PATH_LOCAL_CHROME_DRIVER = "C:/Users/nisha/Documents/Internship/Work/jars/chromeDriver";
public static final String PATH_LOCAL_CHROME_DRIVER = "C:/Users/nisha/Documents/Internship/Work/jars/chromeDriver/chromedriver.exe";
public static final String PATH_GH_ACTION_CHROME_DRIVER = "/usr/bin/chromedriver";
public static final String PATH_LOCAL_WORKUNITS_JSON = "C:/Users/nisha/Documents/Internship/Work/files/workunits.json";
public static final String PATH_GH_ACTION_WORKUNITS_JSON = "";
public static final String LOCAL_SELENIUM_SERVER = "http://localhost:4444/wd/hub";
public static final String LOCAL_IP = "http://192.168.0.221:8010/";
public static final String GITHUB_ACTION_IP = "http://127.0.0.1:8010/";
public static final String ACTIVITIES_URL = "esp/files/index.html#/activities";
public static final String ECL_WORK_UNITS_URL = "esp/files/index.html#/workunits";
public static final int[] dropdownValues = {10, 25, 50, 100, 250, 500, 1000};
public static final int MALFORMED_TIME_STRING = -1;
public static final int WAIT_TIME_IN_SECONDS = 4;
public static final int WAIT_TIME_IN_SECONDS = 1;
public static final int WAIT_TIME_THRESHOLD_IN_SECONDS = 20;
public static final String TEST_DESCRIPTION_TEXT = "Testing Description";
public static final boolean TEST_DETAIL_PAGE_FIELD_NAMES_ALL = true;
public static final boolean TEST_WU_DETAIL_PAGE_DESCRIPTION_ALL = true;
public static final boolean TEST_WU_DETAIL_PAGE_PROTECTED_ALL = true;
public static final boolean TEST_DETAIL_PAGE_TAB_CLICK_ALL = true;

// these values are set in the beginning in the TestRunner.java file
public static String PATH_FOLDER_JSON = "";
public static String LOG_LEVEL = "";

public static final String WORKUNITS_JSON_FILE_NAME = "workunits.json";
public static final String DFU_WORKUNITS_JSON_FILE_NAME = "dfu-workunits.json";
public static final String FILES_JSON_FILE_NAME = "files.json";
}
4 changes: 3 additions & 1 deletion esp/src/test-ui/tests/framework/config/TestClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

public class TestClasses {

// ActivitiesTest class should always be the first class to load, as it gets URLs for all other pages.

public static final List<TestClass> testClassesList = List.of(
//new TestClass("ActivitiesTest", "framework.pages.ActivitiesTest"),
new TestClass("ActivitiesTest", "framework.pages.ActivitiesTest"),
new TestClass("ECLWorkUnitsTest", "framework.pages.ECLWorkUnitsTest")
);
}
Loading

0 comments on commit 8c5d558

Please sign in to comment.