commit a08867560c8d370a0469e4d5d2dc81c94bbbc45e Author: Abdullah Bin Masood Siddiqui Date: Fri Oct 31 20:27:32 2025 +0500 Initial commit: Test Automation Framework for Seller Horizon diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b233c6e --- /dev/null +++ b/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + + com.utopiadeals + scat + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + 5.10.2 + 1.49.0 + 2.17.2 + 3.2.5 + 2.23.1 + 5.1.2 + 2.0.13 + 1.5.6 + + + + + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + + + + + org.junit.jupiter + junit-jupiter-params + ${junit.jupiter.version} + + + + + com.microsoft.playwright + playwright + ${playwright.version} + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + junit + junit + 4.13.2 + compile + + + org.junit.jupiter + junit-jupiter-api + 5.10.2 + compile + + + + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + com.aventstack + extentreports + ${extent.reports.versions} + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + + + + + + src/test/resources + true + + **/* + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.version} + + + false + + **/*Test.java + **/*Tests.java + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 + + 0 + + + + + + + dev + + https://app.sellercosmos.com/ + ${env.browser} + ${env.headless} + + + + + qa + + https://app.sellercosmos.com/ + chrome + false + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/utopiadeals/framework/BrowserContextManager.java b/src/main/java/com/utopiadeals/framework/BrowserContextManager.java new file mode 100644 index 0000000..3a966b9 --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/BrowserContextManager.java @@ -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 contextThreadLocal = new ThreadLocal<>(); + protected static final ThreadLocal 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(); + } + + + + +} diff --git a/src/main/java/com/utopiadeals/framework/BrowserFactory.java b/src/main/java/com/utopiadeals/framework/BrowserFactory.java new file mode 100644 index 0000000..54e44cb --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/BrowserFactory.java @@ -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 getAllValues() { + Set values = new HashSet<>(); + for (BrowserFactory id : BrowserFactory.values()) { + values.add(id.getValue()); + } + return values; + } + + public abstract BrowserContextManager getBrowserContextManager(boolean isHeadless); + +} diff --git a/src/main/java/com/utopiadeals/framework/ChromeContext.java b/src/main/java/com/utopiadeals/framework/ChromeContext.java new file mode 100644 index 0000000..2cb28a4 --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/ChromeContext.java @@ -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; + } + +} diff --git a/src/main/java/com/utopiadeals/framework/FirefoxContext.java b/src/main/java/com/utopiadeals/framework/FirefoxContext.java new file mode 100644 index 0000000..44b1cda --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/FirefoxContext.java @@ -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; + } + +} \ No newline at end of file diff --git a/src/main/java/com/utopiadeals/framework/UnsupportedBrowserException.java b/src/main/java/com/utopiadeals/framework/UnsupportedBrowserException.java new file mode 100644 index 0000000..961ad37 --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/UnsupportedBrowserException.java @@ -0,0 +1,9 @@ +package com.utopiadeals.framework; + +public class UnsupportedBrowserException extends RuntimeException { + + public UnsupportedBrowserException(String browserId) { + super("Unsupported browser: " + browserId); + } + +} diff --git a/src/main/java/com/utopiadeals/framework/annotations/JsonTestDataExtension.java b/src/main/java/com/utopiadeals/framework/annotations/JsonTestDataExtension.java new file mode 100644 index 0000000..9413202 --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/annotations/JsonTestDataExtension.java @@ -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 ""; +} diff --git a/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java b/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java new file mode 100644 index 0000000..30a1c08 --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/extensions/ExtentReportExtension.java @@ -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 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 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); + } + } + + +} diff --git a/src/main/java/com/utopiadeals/framework/extensions/JsonTestDataArgumentsProvider.java b/src/main/java/com/utopiadeals/framework/extensions/JsonTestDataArgumentsProvider.java new file mode 100644 index 0000000..14caf4a --- /dev/null +++ b/src/main/java/com/utopiadeals/framework/extensions/JsonTestDataArgumentsProvider.java @@ -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 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 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); + } + } +} + diff --git a/src/main/java/com/utopiadeals/utils/FileIoHelper.java b/src/main/java/com/utopiadeals/utils/FileIoHelper.java new file mode 100644 index 0000000..5e76b13 --- /dev/null +++ b/src/main/java/com/utopiadeals/utils/FileIoHelper.java @@ -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); + } + } +} diff --git a/src/main/java/com/utopiadeals/utils/PropertyReader.java b/src/main/java/com/utopiadeals/utils/PropertyReader.java new file mode 100644 index 0000000..af5b8cb --- /dev/null +++ b/src/main/java/com/utopiadeals/utils/PropertyReader.java @@ -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); + } +} diff --git a/src/main/java/com/utopiadeals/utils/TestDataProvider.java b/src/main/java/com/utopiadeals/utils/TestDataProvider.java new file mode 100644 index 0000000..b782005 --- /dev/null +++ b/src/main/java/com/utopiadeals/utils/TestDataProvider.java @@ -0,0 +1,31 @@ +package com.utopiadeals.utils; + +import java.util.Map; + +public class TestDataProvider { + + private final Map testData; + + public TestDataProvider(Map 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); + } +} diff --git a/src/main/java/com/utopiadeals/utils/config/Constants.java b/src/main/java/com/utopiadeals/utils/config/Constants.java new file mode 100644 index 0000000..612a542 --- /dev/null +++ b/src/main/java/com/utopiadeals/utils/config/Constants.java @@ -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"); + } + +} diff --git a/src/main/java/com/utopiadeals/web/pages/BasePage.java b/src/main/java/com/utopiadeals/web/pages/BasePage.java new file mode 100644 index 0000000..6e3bed0 --- /dev/null +++ b/src/main/java/com/utopiadeals/web/pages/BasePage.java @@ -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; + } +} diff --git a/src/main/java/com/utopiadeals/web/pages/LoginPage.java b/src/main/java/com/utopiadeals/web/pages/LoginPage.java new file mode 100644 index 0000000..e8fc7bc --- /dev/null +++ b/src/main/java/com/utopiadeals/web/pages/LoginPage.java @@ -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); + } + + +} diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..c1e4139 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,36 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + + + logs/application.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + UTF-8 + + + logs/application-%d{yyyy-MM-dd}.%i.log + 10MB + 30 + 1GB + + + + + + + + + + + + + + + diff --git a/src/test/java/testsuites/LoginTest.java b/src/test/java/testsuites/LoginTest.java new file mode 100644 index 0000000..55a99db --- /dev/null +++ b/src/test/java/testsuites/LoginTest.java @@ -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"); + } + +} diff --git a/src/test/java/testsuites/WebTestSuiteBase.java b/src/test/java/testsuites/WebTestSuiteBase.java new file mode 100644 index 0000000..fabd038 --- /dev/null +++ b/src/test/java/testsuites/WebTestSuiteBase.java @@ -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); + } + + +} diff --git a/src/test/resources/config/application.properties b/src/test/resources/config/application.properties new file mode 100644 index 0000000..66e3b46 --- /dev/null +++ b/src/test/resources/config/application.properties @@ -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 \ No newline at end of file diff --git a/src/test/resources/test-data/test-data.json b/src/test/resources/test-data/test-data.json new file mode 100644 index 0000000..b579200 --- /dev/null +++ b/src/test/resources/test-data/test-data.json @@ -0,0 +1,16 @@ +{ + "testMethods": { + "loginTest": { + "dataSets": { + "dataSet-0": { + "username": "utest@utopiadeals.com", + "password": "utest0001" + }, + "dataSet-1": { + "username": "abdullah@utopiadeals.com", + "password": "Utopia01" + } + } + } + } +} \ No newline at end of file