Initial commit: Test Automation Framework for Seller Horizon
commit
a08867560c
|
|
@ -0,0 +1,38 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<?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>
|
||||
|
||||
<groupId>com.utopiadeals</groupId>
|
||||
<artifactId>scat</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<junit.jupiter.version>5.10.2</junit.jupiter.version>
|
||||
<playwright.version>1.49.0</playwright.version>
|
||||
<jackson.version>2.17.2</jackson.version>
|
||||
<surefire.version>3.2.5</surefire.version>
|
||||
<log4j.version>2.23.1</log4j.version>
|
||||
<extent.reports.versions>5.1.2</extent.reports.versions>
|
||||
<slf4j.version>2.0.13</slf4j.version>
|
||||
<logback.version>1.5.6</logback.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- JUnit 5 -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit 5 Params -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Playwright Java -->
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>${playwright.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson Databind (JSON parsing) -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Optional: JSON annotations -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.10.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Loggers ! -->
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aventstack</groupId>
|
||||
<artifactId>extentreports</artifactId>
|
||||
<version>${extent.reports.versions}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J API -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Logback Implementation -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SLF4J Simple Binding (for tests) -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/*</include> <!-- All files in all subdirectories -->
|
||||
</includes>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<plugins>
|
||||
<!-- Surefire Plugin for running tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${surefire.version}</version>
|
||||
<configuration>
|
||||
<!-- Enable JUnit 5 Platform -->
|
||||
<useModulePath>false</useModulePath>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<argLine>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005</argLine>
|
||||
<!-- 👇 Optional: Run in same JVM for better debugging -->
|
||||
<forkCount>0</forkCount>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev</id>
|
||||
<properties>
|
||||
<seller.cosmos.url>https://app.sellercosmos.com/</seller.cosmos.url>
|
||||
<env.browser>${env.browser}</env.browser>
|
||||
<env.headless>${env.headless}</env.headless>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>qa</id>
|
||||
<properties>
|
||||
<seller.cosmos.url>https://app.sellercosmos.com/</seller.cosmos.url>
|
||||
<env.browser>chrome</env.browser>
|
||||
<env.headless>false</env.headless>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.utopiadeals.framework;
|
||||
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
|
||||
public abstract class BrowserContextManager {
|
||||
|
||||
protected Playwright playwright;
|
||||
private final boolean isHeadless;
|
||||
protected static final ThreadLocal<BrowserContext> contextThreadLocal = new ThreadLocal<>();
|
||||
protected static final ThreadLocal<Page> pageThreadLocal = new ThreadLocal<>();
|
||||
|
||||
public BrowserContextManager(boolean isHeadless) {
|
||||
this.playwright = Playwright.create(); // Initialize Playwright here
|
||||
this.isHeadless = isHeadless;
|
||||
}
|
||||
|
||||
public void initBrowserContext() {
|
||||
if (contextThreadLocal.get() == null) {
|
||||
BrowserContext browserContext = createBrowserContext();
|
||||
contextThreadLocal.set(browserContext);
|
||||
pageThreadLocal.set(contextThreadLocal.get().newPage());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract BrowserContext createBrowserContext();
|
||||
|
||||
|
||||
public BrowserType.LaunchOptions getLaunchOptions() {
|
||||
BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions();
|
||||
launchOptions.setHeadless(this.isHeadless);
|
||||
return launchOptions;
|
||||
}
|
||||
|
||||
|
||||
public static BrowserContext getBrowserContext() {
|
||||
return contextThreadLocal.get();
|
||||
}
|
||||
|
||||
public static Page getPage() {
|
||||
if (contextThreadLocal.get() == null) {
|
||||
throw new IllegalStateException("Browser context is not created.");
|
||||
}
|
||||
return pageThreadLocal.get(); // Creating and returning a new Page
|
||||
}
|
||||
|
||||
public void closeBrowserContext() {
|
||||
if (contextThreadLocal.get() == null) {
|
||||
throw new IllegalStateException("Browser context is not created.");
|
||||
}
|
||||
contextThreadLocal.get().close();
|
||||
contextThreadLocal.remove();
|
||||
}
|
||||
|
||||
public void closePlaywright() {
|
||||
playwright.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.utopiadeals.framework;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum BrowserFactory {
|
||||
|
||||
CHROME("chrome") {
|
||||
@Override
|
||||
public BrowserContextManager getBrowserContextManager(boolean isHeadless) {
|
||||
return new ChromeContext(isHeadless);
|
||||
}
|
||||
},
|
||||
FIREFOX("firefox") {
|
||||
@Override
|
||||
public BrowserContextManager getBrowserContextManager(boolean isHeadless) {
|
||||
return new FirefoxContext(isHeadless);
|
||||
}
|
||||
};
|
||||
// EDGE {
|
||||
// @Override
|
||||
// public BrowserContextManager getBrowserContextManager() {
|
||||
// return new EdgeDriverManager();
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// SAFARI {
|
||||
// @Override
|
||||
// public BrowserContextManager getBrowserContextManager() {
|
||||
// return new SafariDriverManager();
|
||||
// }
|
||||
// };
|
||||
|
||||
private final String value;
|
||||
|
||||
BrowserFactory(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static BrowserFactory fromValue(String strValue) throws RuntimeException {
|
||||
for (BrowserFactory browserId : BrowserFactory.values()) {
|
||||
if (browserId.getValue().equals(strValue)) {
|
||||
return browserId;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public static Set<String> getAllValues() {
|
||||
Set<String> values = new HashSet<>();
|
||||
for (BrowserFactory id : BrowserFactory.values()) {
|
||||
values.add(id.getValue());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
public abstract BrowserContextManager getBrowserContextManager(boolean isHeadless);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.utopiadeals.framework;
|
||||
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
|
||||
public class ChromeContext extends BrowserContextManager {
|
||||
|
||||
public ChromeContext(boolean isHeadless) {
|
||||
super(isHeadless);
|
||||
}
|
||||
|
||||
public BrowserContext createBrowserContext() {
|
||||
Browser browser = playwright.chromium().launch(getLaunchOptions());
|
||||
BrowserContext browserContext = browser.newContext(); // Create browser context
|
||||
return browserContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.utopiadeals.framework;
|
||||
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
|
||||
public class FirefoxContext extends BrowserContextManager {
|
||||
|
||||
public FirefoxContext(boolean isHeadless) {
|
||||
super(isHeadless);
|
||||
}
|
||||
|
||||
public static FirefoxContext create(boolean isHeadless) {
|
||||
return new FirefoxContext(isHeadless);
|
||||
}
|
||||
|
||||
|
||||
public BrowserContext createBrowserContext() {
|
||||
Browser browser = playwright.firefox().launch(getLaunchOptions());
|
||||
BrowserContext browserContext = browser.newContext(); // Create browser context
|
||||
return browserContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.utopiadeals.framework;
|
||||
|
||||
public class UnsupportedBrowserException extends RuntimeException {
|
||||
|
||||
public UnsupportedBrowserException(String browserId) {
|
||||
super("Unsupported browser: " + browserId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.utopiadeals.framework.annotations;
|
||||
|
||||
import com.utopiadeals.framework.extensions.JsonTestDataArgumentsProvider;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ArgumentsSource(JsonTestDataArgumentsProvider.class)
|
||||
public @interface JsonTestDataExtension {
|
||||
String value() default "";
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package com.utopiadeals.framework.extensions;
|
||||
|
||||
import com.aventstack.extentreports.AnalysisStrategy;
|
||||
import com.aventstack.extentreports.ExtentReports;
|
||||
import com.aventstack.extentreports.ExtentTest;
|
||||
import com.aventstack.extentreports.MediaEntityBuilder;
|
||||
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
|
||||
import com.aventstack.extentreports.reporter.configuration.ExtentSparkReporterConfig;
|
||||
import com.aventstack.extentreports.reporter.configuration.Theme;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.options.ScreenshotType;
|
||||
import com.utopiadeals.framework.BrowserContextManager;
|
||||
import org.junit.jupiter.api.extension.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ExtentReportExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, TestWatcher, TestExecutionExceptionHandler {
|
||||
|
||||
private static ExtentReports extent;
|
||||
private static final ThreadLocal<ExtentTest> test = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws Exception {
|
||||
if (extent == null) {
|
||||
createExtentReports();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterAll(ExtensionContext context) throws Exception {
|
||||
if (extent != null) {
|
||||
extent.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
|
||||
// This is called WHEN the exception occurs, before @AfterEach
|
||||
captureFailureScreenshot(test.get(),throwable.getCause());
|
||||
throw throwable; // Re-throw to maintain normal failure flow
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
String testName = context.getDisplayName();
|
||||
String className = context.getTestClass().map(Class::getSimpleName).orElse("Unknown");
|
||||
|
||||
ExtentTest extentTest = extent.createTest(testName)
|
||||
.assignCategory(className);
|
||||
test.set(extentTest);
|
||||
}
|
||||
|
||||
private void createExtentReports() {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy_MMM_dd_HH_mm_ss", Locale.US);
|
||||
String reportName = "TestExecutionReport - " + "SellerHorizon" + " - " +
|
||||
LocalDateTime.now().format(dateTimeFormatter) + ".html";
|
||||
|
||||
File extentReportOutputFile = new File("./target/test-output/extent-reports", reportName);
|
||||
ExtentSparkReporter extentSparkReporter = new ExtentSparkReporter(extentReportOutputFile);
|
||||
//-- Programmatically configure Extent Report. This is a better practice as opposed to configuring via a configuration file.
|
||||
ExtentSparkReporterConfig extentSparkReporterConfig = extentSparkReporter.config();
|
||||
extentSparkReporterConfig.enableOfflineMode(true);
|
||||
extentSparkReporterConfig.setDocumentTitle("Test Execution Report");
|
||||
extentSparkReporterConfig.setEncoding("utf-8");
|
||||
//TODO-Report: Version may be incorporated in report name either from application.properties
|
||||
// extentSparkReporterConfig.setReportName("TARGET APPLICATION: " +
|
||||
// ("SellerHorizon") + " VERSION: " +
|
||||
// (targetApplicationVersion == null ? "[NOT SPECIFIED]" : targetApplicationVersion));
|
||||
extentSparkReporterConfig.setTheme(Theme.STANDARD);
|
||||
extentSparkReporterConfig.setTimelineEnabled(true);
|
||||
// extentSparkReporterConfig.setTimeStampFormat();
|
||||
|
||||
ExtentReports extentReports = new ExtentReports();
|
||||
extentReports.attachReporter(extentSparkReporter);
|
||||
extentReports.setAnalysisStrategy(AnalysisStrategy.SUITE);
|
||||
|
||||
extent = extentReports;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void testDisabled(ExtensionContext context, Optional<String> reason) {
|
||||
test.get().skip(reason.orElse("Test disabled"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testSuccessful(ExtensionContext context) {
|
||||
test.get().pass("Test executed successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAborted(ExtensionContext context, Throwable cause) {
|
||||
test.get().skip(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFailed(ExtensionContext context, Throwable cause) {
|
||||
ExtentTest currentTest = test.get();
|
||||
currentTest.fail(cause);
|
||||
}
|
||||
|
||||
private void captureFailureScreenshot(ExtentTest extentTest, Throwable cause) {
|
||||
try {
|
||||
Page page = BrowserContextManager.getPage();
|
||||
if (page != null && !page.isClosed()) {
|
||||
// Take screenshot
|
||||
byte[] screenshot = page.screenshot(new Page.ScreenshotOptions()
|
||||
.setFullPage(true)
|
||||
.setType(ScreenshotType.PNG));
|
||||
String base64Screenshot = java.util.Base64.getEncoder().encodeToString(screenshot);
|
||||
|
||||
// Attach screenshot to the failed test
|
||||
extentTest.fail(cause,
|
||||
MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot,"Application Screenshot").build()
|
||||
);
|
||||
|
||||
extentTest.info("Page URL: " + page.url());
|
||||
} else {
|
||||
extentTest.fail(cause);
|
||||
extentTest.warning("Could not capture screenshot - page is closed or not available");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// If screenshot fails, still log the failure
|
||||
extentTest.fail(cause);
|
||||
extentTest.warning("Failed to capture screenshot: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Only logging methods - no screenshot methods for manual use
|
||||
public static void logInfo(String message) {
|
||||
if (test.get() != null) {
|
||||
test.get().info(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void logPass(String message) {
|
||||
if (test.get() != null) {
|
||||
test.get().pass(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void logWarning(String message) {
|
||||
if (test.get() != null) {
|
||||
test.get().warning(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package com.utopiadeals.framework.extensions;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.utopiadeals.framework.annotations.JsonTestDataExtension;
|
||||
import com.utopiadeals.utils.FileIoHelper;
|
||||
import com.utopiadeals.utils.TestDataProvider;
|
||||
import com.utopiadeals.utils.config.Constants;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Provides test data from JSON files for parameterized tests.
|
||||
* The test data file is expected to be in the resources/test-data directory.
|
||||
* Expected JSON structure:
|
||||
* {
|
||||
* "testMethods": {
|
||||
* "loginTest": {
|
||||
* "dataSets": {
|
||||
* "dataSet-0": {
|
||||
* "username": "user1@example.com",
|
||||
* "password": "pass1"
|
||||
* },
|
||||
* "dataSet-1": {
|
||||
* "username": "user2@example.com",
|
||||
* "password": "pass2"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public class JsonTestDataArgumentsProvider implements ArgumentsProvider {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
private static final JsonNode rootNode = FileIoHelper.loadJsonFile(Constants.getTestDataPath());
|
||||
private static final String TEST_METHODS_NODE = "testMethods";
|
||||
private static final String DATA_SETS_NODE = "dataSets";
|
||||
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||
String testMethodName = context.getRequiredTestMethod().getName();
|
||||
String[] datasetNames = getDatasetsValue(context);
|
||||
|
||||
// Get the test method node
|
||||
JsonNode testMethodsNode = rootNode.path(TEST_METHODS_NODE);
|
||||
if (testMethodsNode.isMissingNode()) {
|
||||
throw new IllegalStateException("No 'testMethods' node found in test data file");
|
||||
}
|
||||
|
||||
JsonNode testMethodNode = testMethodsNode.path(testMethodName);
|
||||
if (testMethodNode.isMissingNode()) {
|
||||
throw new IllegalArgumentException("No test data found for method: " + testMethodName);
|
||||
}
|
||||
|
||||
JsonNode dataSetsNode = testMethodNode.path(DATA_SETS_NODE);
|
||||
if (dataSetsNode.isMissingNode()) {
|
||||
throw new IllegalStateException("No 'dataSets' node found for method: " + testMethodName);
|
||||
}
|
||||
|
||||
return Arrays.stream(datasetNames)
|
||||
.map(String::trim)
|
||||
.filter(datasetName -> !datasetName.isEmpty())
|
||||
.map(datasetName -> {
|
||||
JsonNode dataSet = dataSetsNode.path(datasetName);
|
||||
if (dataSet.isMissingNode()) {
|
||||
throw new IllegalArgumentException("Dataset not found: " + datasetName +
|
||||
" for test method: " + testMethodName);
|
||||
}
|
||||
return createTestDataProvider(dataSet);
|
||||
})
|
||||
.map(Arguments::of);
|
||||
}
|
||||
|
||||
private String[] getDatasetsValue(ExtensionContext context) {
|
||||
return context.getRequiredTestMethod()
|
||||
.getAnnotation(JsonTestDataExtension.class)
|
||||
.value()
|
||||
.split(",");
|
||||
}
|
||||
|
||||
private TestDataProvider createTestDataProvider(JsonNode dataSetNode) {
|
||||
try {
|
||||
Map<String, Object> testData = new HashMap<>();
|
||||
dataSetNode.fields().forEachRemaining(entry -> {
|
||||
JsonNode value = entry.getValue();
|
||||
// Store the appropriate type based on the JSON node type
|
||||
if (value.isTextual()) {
|
||||
testData.put(entry.getKey(), value.asText());
|
||||
} else if (value.isNumber()) {
|
||||
testData.put(entry.getKey(), value.numberValue());
|
||||
} else if (value.isBoolean()) {
|
||||
testData.put(entry.getKey(), value.booleanValue());
|
||||
} else {
|
||||
testData.put(entry.getKey(), value);
|
||||
}
|
||||
});
|
||||
|
||||
return new TestDataProvider(testData);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create TestDataProvider from JSON: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
package com.utopiadeals.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Properties;
|
||||
|
||||
import static java.lang.ClassLoader.getSystemResourceAsStream;
|
||||
|
||||
public class FileIoHelper {
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Loads properties from a file in the classpath
|
||||
* @param filePath Path to the properties file in the classpath
|
||||
* @return Properties object containing the loaded properties
|
||||
*/
|
||||
public static Properties loadPropertiesFromClasspath(String filePath) {
|
||||
Properties properties = new Properties();
|
||||
try (InputStream in = FileIoHelper.class.getClassLoader().getResourceAsStream(filePath)) {
|
||||
if (in != null) {
|
||||
properties.load(in);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load properties from: " + filePath, e);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a JSON file from the classpath or file system
|
||||
* @param filePath Path to the JSON file (can be in classpath or filesystem)
|
||||
* @return JsonNode containing the parsed JSON
|
||||
*/
|
||||
public static JsonNode loadJsonFile(String filePath) {
|
||||
try {
|
||||
// Try to load from classpath first
|
||||
try (InputStream inputStream = FileIoHelper.class.getClassLoader().getResourceAsStream(filePath)) {
|
||||
if (inputStream != null) {
|
||||
return mapper.readTree(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to file system
|
||||
return mapper.readTree(new File(filePath));
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load JSON file: " + filePath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.utopiadeals.utils;
|
||||
|
||||
import com.utopiadeals.utils.config.Constants;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class PropertyReader {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final Properties properties;
|
||||
|
||||
/**
|
||||
* Instantiates a new Property reader with the provided properties file
|
||||
*/
|
||||
public PropertyReader() {
|
||||
LOGGER.debug("Loading properties from config/{}", Constants.PROPERTIES_NAME);
|
||||
this.properties = FileIoHelper.loadPropertiesFromClasspath(Constants.PROPERTIES_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets string property value by name
|
||||
*
|
||||
* @param propertyName the property name
|
||||
* @return the string value or null if not found
|
||||
*/
|
||||
public String getString(String propertyName) {
|
||||
return properties.getProperty(propertyName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.utopiadeals.utils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TestDataProvider {
|
||||
|
||||
private final Map<String, Object> testData;
|
||||
|
||||
public TestDataProvider(Map<String, Object> testData) {
|
||||
this.testData = testData;
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
Object value = testData.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
public Integer getInt(String key) {
|
||||
Object value = testData.get(key);
|
||||
return value != null ? ((Number) value).intValue() : null;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String key) {
|
||||
Object value = testData.get(key);
|
||||
return value != null ? Boolean.parseBoolean(value.toString()) : null;
|
||||
}
|
||||
|
||||
public Object get(String key) {
|
||||
return testData.get(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.utopiadeals.utils.config;
|
||||
|
||||
import com.utopiadeals.utils.PropertyReader;
|
||||
|
||||
public class Constants {
|
||||
public static final String PROPERTIES_NAME = "config/application.properties";
|
||||
private static PropertyReader props = new PropertyReader();
|
||||
|
||||
public static boolean shouldBrowserRunHeadless() {
|
||||
String headless = props.getString("env.headless");
|
||||
return (headless != null) && (headless.equals("true"));
|
||||
}
|
||||
|
||||
public static String getBrowser() {
|
||||
return props.getString("env.browser");
|
||||
}
|
||||
|
||||
public static String getEnvUrl() {
|
||||
return props.getString("env.url");
|
||||
}
|
||||
|
||||
public static String getTestDataPath() {
|
||||
return props.getString("test.data.path");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.utopiadeals.web.pages;
|
||||
|
||||
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.Response;
|
||||
|
||||
import static com.utopiadeals.framework.BrowserContextManager.getBrowserContext;
|
||||
import static com.utopiadeals.framework.BrowserContextManager.getPage;
|
||||
|
||||
public class BasePage {
|
||||
|
||||
private final BrowserContext browserContext;
|
||||
private final Page page;
|
||||
private Response pageResponse;
|
||||
|
||||
public BasePage() {
|
||||
this.browserContext = getBrowserContext();
|
||||
this.page = getPage();
|
||||
}
|
||||
|
||||
public void navigate(String url) {
|
||||
this.pageResponse = page.navigate(url);
|
||||
}
|
||||
|
||||
|
||||
protected Locator locator(String selector) {
|
||||
return page.locator(selector);
|
||||
}
|
||||
|
||||
protected Locator getByText(String text) {
|
||||
return page.getByText(text);
|
||||
}
|
||||
|
||||
protected void click(Locator locator) {
|
||||
locator.click();
|
||||
// Auto-waiting built-in - no need for explicit waits!
|
||||
}
|
||||
|
||||
protected void clear(Locator locator) {
|
||||
locator.clear();
|
||||
}
|
||||
|
||||
protected void fill(Locator locator, String text) {
|
||||
locator.fill(text);
|
||||
}
|
||||
|
||||
protected String getText(Locator locator) {
|
||||
return locator.textContent().trim();
|
||||
}
|
||||
|
||||
protected boolean isVisible(Locator locator) {
|
||||
return locator.isVisible();
|
||||
}
|
||||
|
||||
public Response getPageResponse() {
|
||||
return pageResponse;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.utopiadeals.web.pages;
|
||||
|
||||
|
||||
import com.microsoft.playwright.Locator;
|
||||
|
||||
public class LoginPage extends BasePage {
|
||||
|
||||
private final String emailFieldLocator = "#email";
|
||||
private final String passwordFieldLocator = "#password";
|
||||
private final String loginButtonLocator = "button[type=submit]";
|
||||
|
||||
|
||||
public void login(String email, String password) {
|
||||
clear(getEmailFieldLocator());
|
||||
fill(getEmailFieldLocator(), email);
|
||||
clear(getPasswordFieldLocator());
|
||||
fill(getPasswordFieldLocator(), password);
|
||||
click(getLoginButtonLocator());
|
||||
}
|
||||
|
||||
|
||||
private Locator getEmailFieldLocator() {
|
||||
return locator(emailFieldLocator);
|
||||
}
|
||||
|
||||
private Locator getPasswordFieldLocator() {
|
||||
return locator(passwordFieldLocator);
|
||||
}
|
||||
|
||||
|
||||
private Locator getLoginButtonLocator() {
|
||||
return locator(loginButtonLocator);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- Define the appenders -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>logs/application.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>logs/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
<totalSizeCap>1GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<!-- Set the default log level -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
|
||||
<!-- Custom package logging levels -->
|
||||
<logger name="com.utopiadeals" level="DEBUG"/>
|
||||
<logger name="org.springframework" level="WARN"/>
|
||||
<logger name="org.hibernate" level="WARN"/>
|
||||
<logger name="com.microsoft.playwright" level="INFO"/>
|
||||
</configuration>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package testsuites;
|
||||
|
||||
import com.utopiadeals.framework.annotations.JsonTestDataExtension;
|
||||
import com.utopiadeals.utils.TestDataProvider;
|
||||
import com.utopiadeals.web.pages.LoginPage;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
||||
public class LoginTest extends WebTestSuiteBase{
|
||||
|
||||
|
||||
@DisplayName("Login Test")
|
||||
@ParameterizedTest()
|
||||
@JsonTestDataExtension("dataSet-0,dataSet-1")
|
||||
public void loginTest(TestDataProvider testDataProvider){
|
||||
LoginPage loginPage = new LoginPage();
|
||||
//loginPage.navigate("https://app.sellercosmos.com/");
|
||||
String userName = testDataProvider.getString("username");
|
||||
String password = testDataProvider.getString("password");
|
||||
loginPage.login(userName,password);
|
||||
// loginPage.login("utest@utopiadeals.com222222","utest0001");
|
||||
Assertions.assertTrue(false,"Expecting true but found false");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package testsuites;
|
||||
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.utopiadeals.framework.BrowserContextManager;
|
||||
import com.utopiadeals.framework.BrowserFactory;
|
||||
import com.utopiadeals.framework.extensions.ExtentReportExtension;
|
||||
import com.utopiadeals.utils.config.Constants;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@ExtendWith(ExtentReportExtension.class)
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class WebTestSuiteBase {
|
||||
|
||||
private BrowserContextManager browserContextManager;
|
||||
private final String browserName = Constants.getBrowser();
|
||||
private final String url = Constants.getEnvUrl();
|
||||
private final Logger logger = LoggerFactory.getLogger(WebTestSuiteBase.class);
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public void initializeBrowser() {
|
||||
if (browserContextManager == null) {
|
||||
browserContextManager = BrowserFactory.fromValue(browserName).getBrowserContextManager(
|
||||
Constants.shouldBrowserRunHeadless());
|
||||
}
|
||||
logger.info("Browser initialized");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void createBrowserContext() {
|
||||
if (browserContextManager != null) {
|
||||
browserContextManager.initBrowserContext();
|
||||
getPage().navigate(url);
|
||||
}
|
||||
logger.info("Browser Context initialized");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void closeBrowserContext() {
|
||||
if (browserContextManager != null) {
|
||||
browserContextManager.closeBrowserContext();
|
||||
}
|
||||
logger.info("Browser Context closed");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public void closePlaywrightBrowser() {
|
||||
browserContextManager.closePlaywright();
|
||||
logger.info("Shutting down");
|
||||
}
|
||||
|
||||
private Page getPage() {
|
||||
return BrowserContextManager.getPage();
|
||||
}
|
||||
|
||||
protected void logInfo(String message) {
|
||||
ExtentReportExtension.logInfo(message);
|
||||
}
|
||||
|
||||
protected void logPass(String message) {
|
||||
ExtentReportExtension.logPass(message);
|
||||
}
|
||||
|
||||
protected void logWarning(String message) {
|
||||
ExtentReportExtension.logWarning(message);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
env.browser=${env.browser}
|
||||
env.headless=${env.headless}
|
||||
env.url=${seller.cosmos.url}
|
||||
|
||||
|
||||
|
||||
#https://app.sellercosmos.com/
|
||||
|
||||
|
||||
|
||||
#${env.browser}
|
||||
#
|
||||
base.url=https://sellerboard.com
|
||||
api.base.url=https://api.sellerboard.com
|
||||
|
||||
# Browser Configuration
|
||||
browser.name=chrome
|
||||
viewport.width=1920
|
||||
viewport.height=1080
|
||||
|
||||
# Timeouts
|
||||
default.timeout=30
|
||||
page.load.timeout=60
|
||||
element.wait.timeout=10
|
||||
|
||||
# Test Data
|
||||
test.data.path=test-data/test-data.json
|
||||
admin.username=admin@sellerboard.com
|
||||
admin.password=admin123
|
||||
test.user.email=test@sellerboard.com
|
||||
test.user.password=test123
|
||||
|
||||
# Reporting & Output
|
||||
screenshot.path=test-results/screenshots/
|
||||
video.path=test-results/videos/
|
||||
report.path=test-results/reports/
|
||||
|
||||
# Environment
|
||||
environment=dev
|
||||
log.level=INFO
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"testMethods": {
|
||||
"loginTest": {
|
||||
"dataSets": {
|
||||
"dataSet-0": {
|
||||
"username": "utest@utopiadeals.com",
|
||||
"password": "utest0001"
|
||||
},
|
||||
"dataSet-1": {
|
||||
"username": "abdullah@utopiadeals.com",
|
||||
"password": "Utopia01"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue