diff --git a/.DS_Store b/.DS_Store
index 7c55c11..b4c7136 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/ai-selenium-test-generator/.DS_Store b/ai-selenium-test-generator/.DS_Store
new file mode 100644
index 0000000..add1d71
Binary files /dev/null and b/ai-selenium-test-generator/.DS_Store differ
diff --git a/ai-selenium-test-generator/config.properties b/ai-selenium-test-generator/config.properties
new file mode 100644
index 0000000..9847a1d
--- /dev/null
+++ b/ai-selenium-test-generator/config.properties
@@ -0,0 +1 @@
+OPENAI_API_KEY=
\ No newline at end of file
diff --git a/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_213940.json b/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_213940.json
new file mode 100644
index 0000000..1777369
--- /dev/null
+++ b/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_213940.json
@@ -0,0 +1,57 @@
+{
+ "testCaseName": "Verify Upcoming Anniversaries panel on Buzz in OrangeHRM Demo",
+ "description": "Open the OrangeHRM demo site, log in, navigate to the Buzz section, verify the 'Upcoming Anniversaries' panel is visible, and print the list of people with upcoming anniversaries.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Navigate to URL",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputs": {
+ "url": "https://opensource-demo.orangehrmlive.com/"
+ },
+ "expected": "OrangeHRM login page is displayed with username and password fields visible."
+ },
+ {
+ "stepNumber": 2,
+ "action": "Enter text",
+ "target": "Username input field (name='username')",
+ "inputs": {
+ "text": "Admin"
+ },
+ "expected": "Username field contains the text 'Admin'."
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter text (secure)",
+ "target": "Password input field (name='password')",
+ "inputs": {
+ "text": "admin123"
+ },
+ "expected": "Password field is populated (masked) without validation errors."
+ },
+ {
+ "stepNumber": 4,
+ "action": "Click",
+ "target": "Login button (type='submit')",
+ "expected": "User is logged in and the Dashboard/Home page is displayed; left navigation menu is visible and contains the 'Buzz' menu item."
+ },
+ {
+ "stepNumber": 5,
+ "action": "Click",
+ "target": "Left navigation menu item with visible text 'Buzz'",
+ "expected": "Buzz page loads successfully. Buzz page header or content specific to Buzz is visible."
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify visibility",
+ "target": "Panel with header/title text 'Upcoming Anniversaries' on the Buzz page",
+ "expected": "'Upcoming Anniversaries' panel is present in the DOM and visible to the user."
+ },
+ {
+ "stepNumber": 7,
+ "action": "Extract and print list items",
+ "target": "Employee name elements within the 'Upcoming Anniversaries' panel (e.g., list items under the panel)",
+ "expected": "The names of people with upcoming anniversaries are captured and printed to the test logs. If no entries are present, print 'No upcoming anniversaries'."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_214149.json b/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_214149.json
new file mode 100644
index 0000000..d582e1e
--- /dev/null
+++ b/ai-selenium-test-generator/generated-tests/AI_GeneratedTest_20251113_214149.json
@@ -0,0 +1,147 @@
+{
+ "testCases": [
+ {
+ "testCaseName": "Verify Upcoming Anniversaries panel is visible on Buzz after valid login",
+ "description": "Open OrangeHRM demo site, log in with valid credentials, navigate to Buzz, verify the 'Upcoming Anniversaries' panel is displayed, and print the list of people shown.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Open URL in a browser",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputValues": null,
+ "expectedValidations": [
+ "Login page is displayed",
+ "Username and Password input fields are visible",
+ "Login button is visible"
+ ]
+ },
+ {
+ "stepNumber": 2,
+ "action": "Enter username",
+ "target": "Login page - Username field",
+ "inputValues": {
+ "username": "Admin"
+ },
+ "expectedValidations": [
+ "Username field contains 'Admin'"
+ ]
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter password",
+ "target": "Login page - Password field",
+ "inputValues": {
+ "password": "admin123"
+ },
+ "expectedValidations": [
+ "Password field is populated (masked)"
+ ]
+ },
+ {
+ "stepNumber": 4,
+ "action": "Click the Login button",
+ "target": "Login page - Login button",
+ "inputValues": null,
+ "expectedValidations": [
+ "User is authenticated and navigated to the application (e.g., Dashboard/Home)",
+ "Main navigation sidebar is visible",
+ "'Buzz' menu item is present"
+ ]
+ },
+ {
+ "stepNumber": 5,
+ "action": "Navigate to Buzz",
+ "target": "Main navigation - 'Buzz' menu item",
+ "inputValues": null,
+ "expectedValidations": [
+ "Buzz page loads successfully",
+ "Buzz page header or identifier is visible"
+ ]
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify the 'Upcoming Anniversaries' panel visibility",
+ "target": "Buzz page - 'Upcoming Anniversaries' panel",
+ "inputValues": null,
+ "expectedValidations": [
+ "'Upcoming Anniversaries' panel is present in the Buzz page layout",
+ "Panel title text equals 'Upcoming Anniversaries'",
+ "Panel is visible (not collapsed or hidden)"
+ ]
+ },
+ {
+ "stepNumber": 7,
+ "action": "Capture and print the list of people with upcoming anniversaries",
+ "target": "Buzz page - 'Upcoming Anniversaries' panel - list items (names)",
+ "inputValues": null,
+ "expectedValidations": [
+ "All displayed names within the 'Upcoming Anniversaries' panel are retrieved",
+ "Names are printed/logged in the test output in display order",
+ "If no names are present, print/log 'No upcoming anniversaries'"
+ ]
+ }
+ ]
+ },
+ {
+ "testCaseName": "Negative - Invalid login prevents access to Buzz and Upcoming Anniversaries panel",
+ "description": "Attempt to log in with invalid credentials and verify that access to Buzz (and the 'Upcoming Anniversaries' panel) is not granted.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Open URL in a browser",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputValues": null,
+ "expectedValidations": [
+ "Login page is displayed",
+ "Username and Password input fields are visible",
+ "Login button is visible"
+ ]
+ },
+ {
+ "stepNumber": 2,
+ "action": "Enter username",
+ "target": "Login page - Username field",
+ "inputValues": {
+ "username": "Admin"
+ },
+ "expectedValidations": [
+ "Username field contains 'Admin'"
+ ]
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter invalid password",
+ "target": "Login page - Password field",
+ "inputValues": {
+ "password": "wrongpass"
+ },
+ "expectedValidations": [
+ "Password field is populated (masked)"
+ ]
+ },
+ {
+ "stepNumber": 4,
+ "action": "Click the Login button",
+ "target": "Login page - Login button",
+ "inputValues": null,
+ "expectedValidations": [
+ "Login remains on the Login page",
+ "Error message 'Invalid credentials' is displayed",
+ "Main navigation sidebar is not available"
+ ]
+ },
+ {
+ "stepNumber": 5,
+ "action": "Verify Buzz and 'Upcoming Anniversaries' are not accessible",
+ "target": "Application navigation and content area",
+ "inputValues": null,
+ "expectedValidations": [
+ "'Buzz' menu item is not accessible when not authenticated",
+ "'Upcoming Anniversaries' panel is not displayed",
+ "User cannot navigate to the Buzz page without a valid session"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/pom.xml b/ai-selenium-test-generator/pom.xml
new file mode 100644
index 0000000..114aac6
--- /dev/null
+++ b/ai-selenium-test-generator/pom.xml
@@ -0,0 +1,59 @@
+
+ 4.0.0
+ ai-selenium
+ ai-selenium-test-generator
+ 0.0.1-SNAPSHOT
+
+
+
+ com.openai
+ openai-java
+ 4.6.1
+
+
+
+
+ org.seleniumhq.selenium
+ selenium-java
+ 4.28.1
+
+
+
+
+ org.json
+ json
+ 20250517
+
+
+
+
+ org.testng
+ testng
+ 7.11.0
+ test
+
+
+
+
+ com.aventstack
+ extentreports
+ 5.1.1
+
+
+
+
+
+
+ UTF-8
+ 21
+ 21
+ 21
+
+
+
+
+
+
diff --git a/ai-selenium-test-generator/reports/ExtentReport.html b/ai-selenium-test-generator/reports/ExtentReport.html
new file mode 100644
index 0000000..a86e270
--- /dev/null
+++ b/ai-selenium-test-generator/reports/ExtentReport.html
@@ -0,0 +1,386 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Started
+
Nov 15, 2025 11:34:59 am
+
+
+
+
+
Ended
+
Nov 15, 2025 11:35:12 am
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/.DS_Store b/ai-selenium-test-generator/src/.DS_Store
new file mode 100644
index 0000000..c47cebd
Binary files /dev/null and b/ai-selenium-test-generator/src/.DS_Store differ
diff --git a/ai-selenium-test-generator/src/main/java/Config.java b/ai-selenium-test-generator/src/main/java/Config.java
new file mode 100644
index 0000000..0a9cd2e
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/Config.java
@@ -0,0 +1,15 @@
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class Config {
+ public static String getApiKey() {
+ try {
+ Properties props = new Properties();
+ props.load(new FileInputStream("config.properties"));
+ return props.getProperty("OPENAI_API_KEY");
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read API key from config file", e);
+ }
+ }
+}
diff --git a/ai-selenium-test-generator/src/main/java/GenerateTestFromExternalizeStory.java b/ai-selenium-test-generator/src/main/java/GenerateTestFromExternalizeStory.java
new file mode 100644
index 0000000..7f732c9
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/GenerateTestFromExternalizeStory.java
@@ -0,0 +1,26 @@
+
+public class GenerateTestFromExternalizeStory {
+
+ public static void main(String[] args) {
+// String story = UserStoryReader.readFromClasspath("user-story.txt");
+// System.out.println(story);
+
+ String resourcePath = "user-story.txt";
+
+ try {
+ System.out.println("Reading user story from resource: " + resourcePath);
+ String story = UserStoryReader.readFromClasspath(resourcePath);
+
+ System.out.println("User story content:\n" + story + "\n");
+ System.out.println("Sending user story to AI...");
+
+ String generatedCode = OpenAIClientWrapper.generateCodeFromStory(story);
+ System.out.println("\nAI Generated Test Code:\n");
+
+ System.out.println(generatedCode);
+ }catch(Exception e) {
+
+ }
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/GenerateTestFromStory.java b/ai-selenium-test-generator/src/main/java/GenerateTestFromStory.java
new file mode 100644
index 0000000..8d2641f
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/GenerateTestFromStory.java
@@ -0,0 +1,36 @@
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+public class GenerateTestFromStory {
+
+ public static void main(String[] args) {
+
+ String userStory = """
+ As a registered user,
+ I want to log in to the website with valid credentials
+ so that I can access my dashboard.
+
+ Acceptance Criteria:
+ - Navigate to https://opensource-demo.orangehrmlive.com/
+ - Verify the Page Title
+ - Enter valid username and password
+ - Verify successful login by checking dashboard visibility or Page Title
+ """;
+
+ try {
+ System.out.println("Sending user story to AI...");
+ String generatedCode = OpenAIClientWrapper.generateCodeFromStory(userStory);
+ System.out.println("\nAI Generated Test Code:\n");
+ // System.out.println(generatedCode);
+
+ // build timestamped filename and save
+ String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
+ String fileName = "generated-tests/AI_GeneratedTest_" + ts + ".java";
+ OpenAIClientWrapper.saveToFile(generatedCode, fileName);
+ System.out.println("\nSaved generated File: " + fileName);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/ai-selenium-test-generator/src/main/java/JsonTestCaseGenerator.java b/ai-selenium-test-generator/src/main/java/JsonTestCaseGenerator.java
new file mode 100644
index 0000000..1be5c4b
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/JsonTestCaseGenerator.java
@@ -0,0 +1,67 @@
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import com.openai.client.OpenAIClient;
+import com.openai.client.okhttp.OpenAIOkHttpClient;
+import com.openai.models.ChatModel;
+import com.openai.models.chat.completions.ChatCompletion;
+import com.openai.models.chat.completions.ChatCompletionCreateParams;
+import com.openai.models.responses.ResponseCreateParams;
+
+public class JsonTestCaseGenerator {
+
+ public static void main(String[] args) {
+ String resourcePath = "user-story.txt";
+
+ System.out.println("Reading user story from resource: " + resourcePath);
+ String userStory = UserStoryReader.readFromClasspath("user-story.txt");
+
+ System.out.println("\nSending user story to AI for JSON Test Case generation...\n");
+
+ try {
+ String jsonOutput = generateJsonTestCase(userStory);
+ //System.out.println("AI generated structured JSON Test Case:\n" + jsonOutput);
+
+ // build timestamped filename and save
+ String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
+ String fileName = "src/test/resources/AI_Generated_JSON_Test.json";
+ OpenAIClientWrapper.saveToFile(jsonOutput, fileName);
+ System.out.println("\nSaved generated File: " + fileName);
+
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ private static String generateJsonTestCase(String userStory) throws IOException{
+
+ String systemPrompt = """
+ You are an expert Test Automation Engineer.
+ Convert the following user story or requirement into a structured JSON Test Case.
+ Use clear fields: testCaseName, description, and steps (each step should include
+ step number, action, target, and any input values or expected validations).
+ Do NOT include code. Only return valid JSON.
+ """;
+
+ String apiKey = Config.getApiKey();
+ OpenAIClient client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
+
+ // Create chat completion parameters
+ ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().model(ChatModel.GPT_5)
+ .addSystemMessage(systemPrompt).addUserMessage("User Story / Acceptance Criteria:\n\n" + userStory)
+ .build();
+
+ // Call the API
+ ChatCompletion result = client.chat().completions().create(params);
+
+ // Extract content
+ String JsonTestCase = result.choices().get(0).message().content()
+ .orElseThrow(() -> new IOException("No content in OpenAI response"));
+
+ return JsonTestCase.trim();
+
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/Main.java b/ai-selenium-test-generator/src/main/java/Main.java
new file mode 100644
index 0000000..43b5b27
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/Main.java
@@ -0,0 +1,22 @@
+import com.openai.client.OpenAIClient;
+import com.openai.client.okhttp.OpenAIOkHttpClient;
+import com.openai.models.responses.Response;
+import com.openai.models.responses.ResponseCreateParams;
+
+public class Main {
+ public static void main(String[] args) {
+
+ //System.out.println("Key: " + System.getenv("OPENAI_API_KEY"));
+
+ // Create client from environment variables
+ OpenAIClient client = OpenAIOkHttpClient.fromEnv();
+
+ ResponseCreateParams params = ResponseCreateParams.builder()
+ .input("Hey, You are free to use the Internet. now tell me, what is the current time and temprature of New Jersey, Use city - Jersey City")
+ .model("gpt-5")
+ .build();
+
+ Response response = client.responses().create(params);
+ System.out.println(response.output());
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/main/java/OpenAIClientWrapper.java b/ai-selenium-test-generator/src/main/java/OpenAIClientWrapper.java
new file mode 100644
index 0000000..eddd64f
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/OpenAIClientWrapper.java
@@ -0,0 +1,141 @@
+import java.util.logging.Logger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+import com.openai.client.OpenAIClient;
+import com.openai.client.okhttp.OpenAIOkHttpClient;
+import com.openai.models.chat.completions.ChatCompletion;
+import com.openai.models.chat.completions.ChatCompletionCreateParams;
+import com.openai.models.ChatModel;
+
+/**
+ *
+ * This class connects to the OpenAI API to generate Selenium + TestNG Java test
+ * cases automatically from user stories or requirements.
+ */
+
+public class OpenAIClientWrapper {
+ private static final Logger LOGGER = Logger.getLogger(OpenAIClientWrapper.class.getName());
+ private static OpenAIClient client;
+
+ public OpenAIClientWrapper() {
+ String apiKey = Config.getApiKey();
+ if (apiKey == null || apiKey.isBlank()) {
+ throw new IllegalArgumentException("API key must not be blank");
+ }
+ this.client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
+ System.out.println(client);
+ }
+
+ private static void initClientIfNeeded() {
+ if (client == null) {
+ synchronized (OpenAIClientWrapper.class) {
+ if (client == null) {
+ String apiKey = Config.getApiKey();
+ if (apiKey == null || apiKey.isBlank()) {
+ throw new IllegalArgumentException("API key must not be blank");
+ }
+ // This is compatible with openai-java 4.6.1 style you used earlier
+ client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
+ LOGGER.info("OpenAI client initialized.");
+ }
+ }
+ }
+ }
+
+ // Generates Java source code for a Selenium + TestNG test based on the given
+ // user story.
+ public static String generateCodeFromStory(String userStory) throws IOException {
+ if (userStory == null || userStory.isBlank()) {
+ throw new IllegalArgumentException("userStory must not be blank");
+ }
+
+ initClientIfNeeded();
+
+ // Construct the system prompt
+ String systemPrompt = """
+ You are an expert Test Automation Engineer. Generate a clean, maintainable Selenium + TestNG test in Java.
+ Provide full Java source code with imports, class, method, ChromeDriver setup and teardown, descriptive naming, and assertions.
+ """;
+
+ // Create chat completion parameters
+ ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().model(ChatModel.GPT_5)
+ .addSystemMessage(systemPrompt).addUserMessage("User Story / Acceptance Criteria:\n\n" + userStory)
+ .build();
+
+ // Call the API
+ ChatCompletion result = client.chat().completions().create(params);
+
+ // Extract content
+ String code = result.choices().get(0).message().content()
+ .orElseThrow(() -> new IOException("No content in OpenAI response"));
+
+ return code.trim();
+ }
+
+
+ // Generates Java source code for a Selenium + TestNG + Extent Report based on the given
+ // user story.
+ public static String generateCodeFromStoryWithReport(String userStory) throws IOException {
+ if (userStory == null || userStory.isBlank()) {
+ throw new IllegalArgumentException("userStory must not be blank");
+ }
+
+ initClientIfNeeded();
+
+ // Construct the system prompt
+ String systemPrompt = """
+ You are an expert Test Automation Engineer. Generate a clean, maintainable Selenium + TestNG test class in Java
+ that includes full Extent Reports integration.
+ Requirements:
+ - Return ONLY valid Java source code (no explanations, no markdown).
+ - Include package, all necessary imports, and a single public class.
+ - Use ChromeDriver in @BeforeClass and quit driver in @AfterClass.
+ - Initialize ExtentReports (ExtentSparkReporter) in @BeforeClass and call extent.flush() in @AfterClass.
+ - Create an ExtentTest for the test and log each logical step using test.info(...), test.pass(...), and test.fail(...).
+ - On exceptions, capture a screenshot, save it under a reports/screenshots folder, and attach it to the report using MediaEntityBuilder.
+ - Implement the test steps derived from the provided input.
+ - Use clear, descriptive class and method names. Keep one @Test method per logical test-case.
+ - Include appropriate TestNG assertions (Assert.assertTrue / Assert.assertFalse / Assert.assertEquals).
+ - Do not include any external library setup instructions or extra commentary — only the Java test class source.
+ """;
+
+ // Create chat completion parameters
+ ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().model(ChatModel.GPT_5)
+ .addSystemMessage(systemPrompt).addUserMessage("User Story / Acceptance Criteria:\n\n" + userStory)
+ .build();
+
+ // Call the API
+ ChatCompletion result = client.chat().completions().create(params);
+
+ // Extract content
+ String code = result.choices().get(0).message().content()
+ .orElseThrow(() -> new IOException("No content in OpenAI response"));
+
+ return code.trim();
+ }
+
+ // Saves the generated code to a .java file.
+ public static void saveToFile(String code, String fileName) throws IOException {
+ if (code == null || code.isBlank()) {
+ throw new IllegalArgumentException("code must not be blank");
+ }
+ if (fileName == null || fileName.isBlank()) {
+ throw new IllegalArgumentException("fileName must not be blank");
+ }
+
+ Path path = Path.of(fileName).toAbsolutePath();
+
+ // Ensure parent directories exist
+ Path parent = path.getParent();
+ if (parent != null && !Files.exists(parent)) {
+ Files.createDirectories(parent);
+ }
+
+ Files.writeString(path, code, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ LOGGER.info("Saved generated code to: " + path);
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/SampleTestOpenAI.java b/ai-selenium-test-generator/src/main/java/SampleTestOpenAI.java
new file mode 100644
index 0000000..4a8a018
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/SampleTestOpenAI.java
@@ -0,0 +1,37 @@
+import com.openai.client.OpenAIClient;
+import com.openai.client.okhttp.OpenAIOkHttpClient;
+import com.openai.models.responses.Response;
+import com.openai.models.responses.ResponseCreateParams;
+import java.util.List;
+
+public class SampleTestOpenAI {
+
+ public static void main(String[] args) {
+ String apiKey = Config.getApiKey();
+ OpenAIClient client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
+
+ // Example user story to generate test cases from
+ String userStory = "As a registered user, I want to reset my password so that I can regain access if I forget it.";
+
+ // Build prompt: be explicit about required output format
+ String prompt = "You are a software QA engineer. Given the following user story, generate 3 test cases in Gherkin format (Given/When/Then).\n"
+ + "Provide each test case with:\n" + "- Title\n" + "- Preconditions\n"
+ + "- Steps in Gherkin (Given/When/Then)\n" + "- Expected result summary\n\n" + "User story:\n"
+ + userStory
+ + "\n\nRespond only with a JSON array of objects with fields: title, preconditions, gherkin, expected.\n";
+
+ ResponseCreateParams params = ResponseCreateParams.builder()
+ .input(prompt).model("gpt-4.1").build();
+
+ try {
+ Response response = client.responses().create(params);
+ // The SDK returns a Response object — print the model output(s)
+ System.out.println("Raw response object: " + response);
+ } catch (Exception e) {
+ System.err.println("Error calling OpenAI: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/SeleniumCodeGenerator.java b/ai-selenium-test-generator/src/main/java/SeleniumCodeGenerator.java
new file mode 100644
index 0000000..111d467
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/SeleniumCodeGenerator.java
@@ -0,0 +1,36 @@
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class SeleniumCodeGenerator {
+
+ public static void main(String[] args) throws IOException {
+
+ String resourcePath = "src/test/resources/AI_Generated_JSON_Test.json";
+
+// System.out.println("Reading the Test Cases from path: " + resourcePath);
+// String userStory = UserStoryReader.readFromClasspath(resourcePath);
+
+ System.out.println("Reading JSON test case file: " + resourcePath);
+ String userStory = Files.readString(Path.of(resourcePath));
+ System.out.println("\nSending Test Cases to AI for Selenium Script generation...\n");
+
+ try {
+ String generatedCode = OpenAIClientWrapper.generateCodeFromStory(userStory);
+ System.out.println("\nAI Generated Test Code:\n");
+ //System.out.println(generatedCode);
+
+ // build timestamped filename and save
+ String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
+ String fileName = "src/test/java/AI_GeneratedTest_" + ts + ".java";
+ OpenAIClientWrapper.saveToFile(generatedCode, fileName);
+ System.out.println("\nSaved generated File: " + fileName);
+
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/SeleniumCodeGeneratorWithExtentReport.java b/ai-selenium-test-generator/src/main/java/SeleniumCodeGeneratorWithExtentReport.java
new file mode 100644
index 0000000..3fce2e8
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/SeleniumCodeGeneratorWithExtentReport.java
@@ -0,0 +1,36 @@
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class SeleniumCodeGeneratorWithExtentReport {
+
+ public static void main(String[] args) throws IOException {
+
+ String resourcePath = "src/test/resources/AI_Generated_JSON_Test.json";
+
+// System.out.println("Reading the Test Cases from path: " + resourcePath);
+// String userStory = UserStoryReader.readFromClasspath(resourcePath);
+
+ System.out.println("Reading JSON test case file: " + resourcePath);
+ String userStory = Files.readString(Path.of(resourcePath));
+ System.out.println("\nSending Test Cases to AI for Selenium Script generation...\n");
+
+ try {
+ String generatedCode = OpenAIClientWrapper.generateCodeFromStoryWithReport(userStory);
+ System.out.println("\nAI Generated Test Code:\n");
+ //System.out.println(generatedCode);
+
+ // build timestamped filename and save
+ String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
+ String fileName = "src/test/java/AI_GeneratedTest_" + ts + ".java";
+ OpenAIClientWrapper.saveToFile(generatedCode, fileName);
+ System.out.println("\nSaved generated File: " + fileName);
+
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/java/UserStoryReader.java b/ai-selenium-test-generator/src/main/java/UserStoryReader.java
new file mode 100644
index 0000000..888e387
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/java/UserStoryReader.java
@@ -0,0 +1,38 @@
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+public class UserStoryReader {
+
+ public static String readFromClasspath(String userStoryPath) {
+
+ if (userStoryPath == null || userStoryPath.isBlank()) {
+ throw new IllegalArgumentException("Given userStoryPath must not be null or empty");
+ }
+
+ try {
+ InputStream inputStream = UserStoryReader.class.getClassLoader().getResourceAsStream(userStoryPath);
+
+ if (inputStream == null) {
+ throw new RuntimeException("File not found in resources: " + userStoryPath);
+ }
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ StringBuilder content = new StringBuilder();
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ content.append(line).append(System.lineSeparator());
+ }
+
+ reader.close();
+ return content.toString().trim();
+
+ } catch (Exception e) {
+ throw new RuntimeException("Error reading file: " + userStoryPath, e);
+ }
+
+ }
+
+}
diff --git a/ai-selenium-test-generator/src/main/resources/user-story.txt b/ai-selenium-test-generator/src/main/resources/user-story.txt
new file mode 100644
index 0000000..a276c70
--- /dev/null
+++ b/ai-selenium-test-generator/src/main/resources/user-story.txt
@@ -0,0 +1,14 @@
+As a registered user,
+I want to log in to the OrangeHRM demo site with valid credentials
+so that I can access my dashboard and verify my personal information.
+
+Acceptance Criteria:
+- Navigate to https://opensource-demo.orangehrmlive.com/
+- Verify the Page Title is "OrangeHRM"
+- Enter valid username and password (username: Admin, password: admin123)
+- Verify successful login by checking that the dashboard page loads
+- Click on the "My Info" menu item in the sidebar
+- Verify that the "Personal Details" section is displayed
+- Read the value of the "First Name" field
+- Print the "First Name" value in the console
+- Assert that the "First Name" value is not empty
diff --git a/ai-selenium-test-generator/src/test/java/OrangeHRMLoginAndVerifyPersonalInfoTest.java b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginAndVerifyPersonalInfoTest.java
new file mode 100644
index 0000000..887c073
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginAndVerifyPersonalInfoTest.java
@@ -0,0 +1,110 @@
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.time.Duration;
+
+public class OrangeHRMLoginAndVerifyPersonalInfoTest {
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+
+ // Locators
+ private final By usernameField = By.cssSelector("input[name='username']");
+ private final By passwordField = By.cssSelector("input[name='password']");
+ private final By loginButton = By.cssSelector("button[type='submit']");
+ private final By dashboardHeader = By.xpath("//header//h6[normalize-space()='Dashboard']");
+ private final By myInfoMenuItem = By.xpath("//span[normalize-space()='My Info']");
+ private final By personalDetailsHeader = By.xpath("//h6[normalize-space()='Personal Details']");
+ private final By firstNameField = By.cssSelector("input[name='firstName']");
+
+ @BeforeClass
+ public void setUp() {
+ ChromeOptions options = new ChromeOptions();
+ // Optional: run headless by setting -Dheadless=true
+ if ("true".equalsIgnoreCase(System.getProperty("headless"))) {
+ options.addArguments("--headless=new");
+ }
+ options.addArguments("--window-size=1920,1080");
+ driver = new ChromeDriver(options); // Selenium Manager will resolve the driver
+ wait = new WebDriverWait(driver, Duration.ofSeconds(15));
+ driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0));
+ }
+
+ @AfterClass
+ public void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ }
+
+ @Test(description = "Login and Verify Personal Information on OrangeHRM Demo")
+ public void testLoginAndVerifyPersonalInformation() {
+ // Step 1: Navigate to URL and validate login fields are present
+ driver.navigate().to("https://opensource-demo.orangehrmlive.com/");
+ WebElement usernameInput = waitForVisible(usernameField);
+ WebElement passwordInput = waitForVisible(passwordField);
+ Assert.assertTrue(usernameInput.isDisplayed() && usernameInput.isEnabled(), "Username field should be present and enabled.");
+ Assert.assertTrue(passwordInput.isDisplayed() && passwordInput.isEnabled(), "Password field should be present and enabled.");
+
+ // Step 2: Verify page title equals 'OrangeHRM'
+ wait.until(ExpectedConditions.titleIs("OrangeHRM"));
+ Assert.assertEquals(driver.getTitle(), "OrangeHRM", "Page title should be 'OrangeHRM'.");
+
+ // Step 3: Enter username and validate
+ usernameInput.clear();
+ usernameInput.sendKeys("Admin");
+ Assert.assertEquals(usernameInput.getAttribute("value"), "Admin", "Username field should contain 'Admin'.");
+
+ // Step 4: Enter password and validate it's populated and masked
+ passwordInput.clear();
+ passwordInput.sendKeys("admin123");
+ String passwordType = passwordInput.getAttribute("type");
+ String passwordValue = passwordInput.getAttribute("value");
+ Assert.assertEquals(passwordType, "password", "Password field type should be 'password' (masked).");
+ Assert.assertTrue(passwordValue != null && !passwordValue.isEmpty(), "Password field should be populated.");
+
+ // Step 5: Click Login and ensure navigation away from login page
+ waitForClickable(loginButton).click();
+
+ // Step 6: Verify Dashboard is displayed
+ WebElement dashboard = waitForVisible(dashboardHeader);
+ Assert.assertEquals(dashboard.getText().trim(), "Dashboard", "Dashboard header should be visible with text 'Dashboard'.");
+ // Optional URL check if present
+ // Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"), "URL should contain '/dashboard' after login.");
+
+ // Step 7: Click 'My Info' from sidebar
+ waitForClickable(myInfoMenuItem).click();
+
+ // Step 8: Verify 'Personal Details' section is displayed
+ WebElement personalDetails = waitForVisible(personalDetailsHeader);
+ Assert.assertEquals(personalDetails.getText().trim(), "Personal Details", "'Personal Details' section header should be visible.");
+
+ // Step 9: Read First Name field value
+ WebElement firstNameInput = waitForVisible(firstNameField);
+ String firstNameValue = firstNameInput.getAttribute("value");
+
+ // Step 10: Print/Log the First Name value
+ System.out.println("Captured First Name value: " + firstNameValue);
+
+ // Step 11: Assert First Name is not empty
+ Assert.assertTrue(firstNameValue != null && firstNameValue.trim().length() > 0, "First Name value should not be empty.");
+ }
+
+ // Helper methods
+ private WebElement waitForVisible(By locator) {
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
+ }
+
+ private WebElement waitForClickable(By locator) {
+ return wait.until(ExpectedConditions.elementToBeClickable(locator));
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTest_Source.java b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTest_Source.java
new file mode 100644
index 0000000..98d8a6e
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTest_Source.java
@@ -0,0 +1,89 @@
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.time.Duration;
+
+public class OrangeHRMLoginTest_Source {
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+
+ private static final String BASE_URL = "https://opensource-demo.orangehrmlive.com/";
+ private static final String VALID_USERNAME = "Admin";
+ private static final String VALID_PASSWORD = "admin123";
+ private static final String EXPECTED_LOGIN_TITLE = "OrangeHRM";
+
+ // Locators
+ private static final By USERNAME_INPUT = By.name("username");
+ private static final By PASSWORD_INPUT = By.name("password");
+ private static final By LOGIN_BUTTON = By.cssSelector("button[type='submit']");
+ private static final By DASHBOARD_HEADER = By.cssSelector("h6.oxd-text.oxd-text--h6.oxd-topbar-header-breadcrumb-module");
+
+ @BeforeClass(alwaysRun = true)
+ public void setUp() {
+ // Selenium 4.6+ will manage the ChromeDriver binary automatically
+ ChromeOptions options = new ChromeOptions();
+ // options.addArguments("--headless=new"); // Uncomment for headless runs in CI
+ driver = new ChromeDriver(options);
+ wait = new WebDriverWait(driver, Duration.ofSeconds(10));
+ driver.manage().window().maximize();
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ }
+
+ @Test(description = "As a registered user, I can log in with valid credentials and access the dashboard")
+ public void userCanLoginWithValidCredentials() {
+ // Navigate to the application
+ driver.get(BASE_URL);
+
+ // Ensure login page is loaded
+ waitForVisible(USERNAME_INPUT);
+
+ // Verify the Page Title
+ String actualLoginTitle = driver.getTitle();
+ Assert.assertEquals(actualLoginTitle, EXPECTED_LOGIN_TITLE, "Login page title should match expected.");
+
+ // Enter valid username and password and submit
+ login(VALID_USERNAME, VALID_PASSWORD);
+
+ // Verify successful login by checking that the Dashboard is visible
+ WebElement dashboardHeading = waitForVisible(DASHBOARD_HEADER);
+ Assert.assertTrue(dashboardHeading.isDisplayed(), "Dashboard heading should be visible after login.");
+ Assert.assertEquals(dashboardHeading.getText().trim(), "Dashboard", "User should land on the Dashboard.");
+
+ // Optional: Also verify URL contains 'dashboard' and title remains consistent
+ Assert.assertTrue(driver.getCurrentUrl().toLowerCase().contains("dashboard"),
+ "Current URL should contain 'dashboard' after login.");
+ Assert.assertEquals(driver.getTitle(), EXPECTED_LOGIN_TITLE, "Page title remains consistent after login.");
+ }
+
+ // Helper methods
+
+ private void login(String username, String password) {
+ WebElement usernameField = waitForVisible(USERNAME_INPUT);
+ WebElement passwordField = waitForVisible(PASSWORD_INPUT);
+ usernameField.clear();
+ usernameField.sendKeys(username);
+ passwordField.clear();
+ passwordField.sendKeys(password);
+ driver.findElement(LOGIN_BUTTON).click();
+ }
+
+ private WebElement waitForVisible(By locator) {
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTests.java b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTests.java
new file mode 100644
index 0000000..73647c0
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/java/OrangeHRMLoginTests.java
@@ -0,0 +1,220 @@
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import com.aventstack.extentreports.ExtentReports;
+
+import java.time.Duration;
+import java.util.List;
+
+public class OrangeHRMLoginTests {
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+ ExtentReports extent;
+
+ // Test data and URLs
+ private static final String BASE_URL = "https://opensource-demo.orangehrmlive.com/";
+ private static final String VALID_USERNAME = "Admin";
+ private static final String VALID_PASSWORD = "admin123";
+ private static final String INVALID_PASSWORD = "wrongpass";
+ private static final String EXPECTED_TITLE = "OrangeHRM";
+
+ // Locators
+ private final By usernameField = By.name("username");
+ private final By passwordField = By.name("password");
+ private final By loginButton = By.cssSelector("button[type='submit']");
+ private final By loginFormContainer = By.cssSelector("div.orangehrm-login-form");
+
+ private final By dashboardHeader = By.xpath("//h6[normalize-space()='Dashboard']");
+ private final By userMenuName = By.cssSelector("p.oxd-userdropdown-name");
+ private final By userMenuAvatar = By.cssSelector("img.oxd-userdropdown-img");
+ private final By logoutLink = By.xpath("//a[normalize-space()='Logout']");
+
+ private final By invalidCredentialsAlert = By.xpath("//p[contains(@class,'oxd-alert-content-text') and contains(normalize-space(),'Invalid credentials')]");
+
+ @BeforeClass
+ public void setUp() {
+ ChromeOptions options = new ChromeOptions();
+ options.addArguments("--start-maximized");
+ // options.addArguments("--headless=new"); // Uncomment for headless execution if needed
+ driver = new ChromeDriver(options);
+ wait = new WebDriverWait(driver, Duration.ofSeconds(15));
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ }
+
+ @BeforeMethod
+ public void navigateToLoginAndEnsureLoggedOut() {
+ driver.get(BASE_URL);
+ // If already logged in, log out to start from the login page for each test
+ if (!isElementVisibleQuick(loginFormContainer)) {
+ if (isElementVisibleQuick(userMenuName) || isElementVisibleQuick(userMenuAvatar)) {
+ openUserMenu();
+ safeClick(logoutLink);
+ }
+ }
+ waitUntilVisible(loginFormContainer);
+ wait.until(ExpectedConditions.titleIs(EXPECTED_TITLE));
+ }
+
+ @Test(description = "Login with valid credentials - verify Dashboard visibility and URL contains /dashboard")
+ public void loginWithValidCredentials_VerifyDashboardVisibility() {
+ // Step 1-2: On login page, verify title
+ Assert.assertEquals(driver.getTitle(), EXPECTED_TITLE, "Login page title should be 'OrangeHRM'.");
+
+ // Step 3-4: Enter username and password
+ type(usernameField, VALID_USERNAME, true);
+ Assert.assertEquals(getAttribute(usernameField, "value"), VALID_USERNAME, "Username field should contain 'Admin'.");
+
+ type(passwordField, VALID_PASSWORD, true);
+ // Can't assert masked content directly; verify field is of type 'password'
+ Assert.assertEquals(getAttribute(passwordField, "type"), "password", "Password field should be masked.");
+
+ // Step 5: Click Login
+ safeClick(loginButton);
+
+ // Step 6: Verify Dashboard header visible
+ waitUntilVisible(dashboardHeader);
+ Assert.assertTrue(isElementVisible(dashboardHeader), "Dashboard header with text 'Dashboard' should be visible.");
+
+ // Step 7: Verify URL contains '/dashboard'
+ wait.until(ExpectedConditions.urlContains("/dashboard"));
+ Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"), "URL should contain '/dashboard' after successful login.");
+ }
+
+ @Test(description = "Login with valid credentials - verify Page Title after login and topbar user menu visible")
+ public void loginWithValidCredentials_VerifyTitleAndTopbar() {
+ // Step 1-2: On login page, verify title
+ Assert.assertEquals(driver.getTitle(), EXPECTED_TITLE, "Login page title should be 'OrangeHRM'.");
+
+ // Step 3-4: Enter username and password
+ type(usernameField, VALID_USERNAME, true);
+ Assert.assertEquals(getAttribute(usernameField, "value"), VALID_USERNAME, "Username field should contain 'Admin'.");
+ type(passwordField, VALID_PASSWORD, true);
+ Assert.assertEquals(getAttribute(passwordField, "type"), "password", "Password field should be masked.");
+
+ // Step 5: Click Login
+ safeClick(loginButton);
+
+ // Step 6: Verify page title remains 'OrangeHRM' after login
+ wait.until(ExpectedConditions.titleIs(EXPECTED_TITLE));
+ Assert.assertEquals(driver.getTitle(), EXPECTED_TITLE, "Page title should remain 'OrangeHRM' after login.");
+
+ // Step 7: Verify user/topbar menu is visible
+ // Accept either avatar or user name as valid topbar/authenticated indicator
+ boolean topbarVisible = waitUntilAnyVisible(userMenuAvatar, userMenuName);
+ Assert.assertTrue(topbarVisible, "User/topbar menu should be visible, indicating an authenticated session.");
+ }
+
+ @Test(description = "Login with invalid password - verify error handling and that user remains on login page")
+ public void loginWithInvalidPassword_VerifyErrorHandling() {
+ // Step 1-2: On login page, verify title
+ Assert.assertEquals(driver.getTitle(), EXPECTED_TITLE, "Login page title should be 'OrangeHRM'.");
+
+ // Step 3-4: Enter username and invalid password
+ type(usernameField, VALID_USERNAME, true);
+ Assert.assertEquals(getAttribute(usernameField, "value"), VALID_USERNAME, "Username field should contain 'Admin'.");
+ type(passwordField, INVALID_PASSWORD, true);
+ Assert.assertEquals(getAttribute(passwordField, "type"), "password", "Password field should be masked.");
+
+ // Step 5: Click Login
+ safeClick(loginButton);
+
+ // Step 6: Verify error message 'Invalid credentials' is displayed
+ waitUntilVisible(invalidCredentialsAlert);
+ Assert.assertTrue(isElementVisible(invalidCredentialsAlert), "Error message 'Invalid credentials' should be displayed.");
+
+ // Step 7: Verify user remains on login page (URL contains '/auth/login')
+ wait.until(ExpectedConditions.urlContains("/auth/login"));
+ Assert.assertTrue(driver.getCurrentUrl().contains("/auth/login"),
+ "User should remain on the login page; URL should contain '/auth/login'.");
+
+ // Step 8: Verify Dashboard is not visible
+ Assert.assertFalse(isElementVisibleQuick(dashboardHeader), "Dashboard should not be visible after failed login.");
+ }
+
+ // Helper methods
+
+ private void type(By locator, String text, boolean clear) {
+ WebElement el = waitUntilVisible(locator);
+ if (clear) el.clear();
+ el.sendKeys(text);
+ }
+
+ private void safeClick(By locator) {
+ WebElement el = waitUntilClickable(locator);
+ el.click();
+ }
+
+ private String getAttribute(By locator, String attribute) {
+ return waitUntilVisible(locator).getAttribute(attribute);
+ }
+
+ private WebElement waitUntilVisible(By locator) {
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
+ }
+
+ private WebElement waitUntilClickable(By locator) {
+ return wait.until(ExpectedConditions.elementToBeClickable(locator));
+ }
+
+ private boolean isElementVisible(By locator) {
+ try {
+ WebElement el = waitUntilVisible(locator);
+ return el.isDisplayed();
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private boolean isElementVisibleQuick(By locator) {
+ try {
+ WebDriverWait shortWait = new WebDriverWait(driver, Duration.ofSeconds(3));
+ WebElement el = shortWait.until(ExpectedConditions.visibilityOfElementLocated(locator));
+ return el.isDisplayed();
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private boolean waitUntilAnyVisible(By... locators) {
+ long end = System.currentTimeMillis() + Duration.ofSeconds(10).toMillis();
+ while (System.currentTimeMillis() < end) {
+ for (By locator : locators) {
+ List elements = driver.findElements(locator);
+ if (!elements.isEmpty() && elements.get(0).isDisplayed()) {
+ return true;
+ }
+ }
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ return false;
+ }
+
+ private void openUserMenu() {
+ // Try avatar first, then user name, to open dropdown menu
+ if (isElementVisibleQuick(userMenuAvatar)) {
+ safeClick(userMenuAvatar);
+ } else {
+ safeClick(userMenuName);
+ }
+ waitUntilVisible(logoutLink);
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/java/OrangeHRMUpcomingAnniversariesTest.java b/ai-selenium-test-generator/src/test/java/OrangeHRMUpcomingAnniversariesTest.java
new file mode 100644
index 0000000..0b702c1
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/java/OrangeHRMUpcomingAnniversariesTest.java
@@ -0,0 +1,231 @@
+import org.openqa.selenium.*;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.annotations.*;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class OrangeHRMUpcomingAnniversariesTest {
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+
+ private static final String BASE_URL = "https://opensource-demo.orangehrmlive.com/";
+ private static final String USERNAME = "Admin";
+ private static final String PASSWORD = "admin123";
+
+ @BeforeClass(alwaysRun = true)
+ public void setUpChromeDriver() {
+ //WebDriverManager.chromedriver().setup();
+ ChromeOptions options = new ChromeOptions();
+ // options.addArguments("--headless=new"); // Uncomment if you want to run headless
+ options.addArguments("--start-maximized");
+ options.addArguments("--disable-gpu");
+ options.addArguments("--no-sandbox");
+ driver = new ChromeDriver(options);
+ wait = new WebDriverWait(driver, Duration.ofSeconds(15));
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void tearDown() {
+ if (driver != null) {
+ try {
+ // Try to log out if still logged in
+ openUserDropdownIfPresent();
+ clickIfPresent(By.xpath("//a[normalize-space()='Logout']"));
+ } catch (Exception ignored) {
+ } finally {
+ driver.quit();
+ }
+ }
+ }
+
+ @Test(description = "Verify Upcoming Anniversaries list on Buzz page and print entries")
+ public void verifyAndPrintUpcomingAnniversaries() {
+ // 1) Open home page
+ driver.get(BASE_URL);
+
+ // 2) Log in with valid credentials
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("username"))).sendKeys(USERNAME);
+ driver.findElement(By.name("password")).sendKeys(PASSWORD);
+ driver.findElement(By.cssSelector("button[type='submit']")).click();
+
+ // Wait for post-login landing (Dashboard visible or sidebar present)
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("div.oxd-sidepanel")));
+
+ // 3) Navigate to Buzz section via sidebar
+ By buzzMenu = By.xpath("//span[normalize-space()='Buzz']/ancestor::a");
+ wait.until(ExpectedConditions.elementToBeClickable(buzzMenu)).click();
+
+ // Wait for Buzz page header to appear
+ wait.until(ExpectedConditions.or(
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//h6[normalize-space()='Buzz']")),
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//h6[contains(normalize-space(),'Buzz')]"))
+ ));
+
+ // 4) Verify "Upcoming Anniversaries" panel is visible (scroll into view if needed)
+ WebElement anniversariesHeader = wait.until(ExpectedConditions.visibilityOfElementLocated(
+ By.xpath("//h6[normalize-space()='Upcoming Anniversaries' or contains(.,'Upcoming Anniversaries')]")
+ ));
+ scrollIntoView(anniversariesHeader);
+
+ // The container/card that holds the panel body
+ WebElement anniversariesPanelContainer = anniversariesHeader.findElement(By.xpath("./ancestor::div[contains(@class,'oxd-card') or contains(@class,'orangehrm')][1]"));
+ Assert.assertTrue(anniversariesPanelContainer.isDisplayed(), "'Upcoming Anniversaries' panel should be visible");
+
+ // 5) If the panel is collapsed or initially empty, try expanding by clicking header
+ List entryCandidates = findAnniversaryEntryContainers(anniversariesPanelContainer);
+ if (entryCandidates.isEmpty()) {
+ safeClick(anniversariesHeader); // attempt to expand
+ // small wait for expand animation/content load
+ sleep(500);
+ entryCandidates = findAnniversaryEntryContainers(anniversariesPanelContainer);
+ }
+
+ // 6) Extract names (and optionally dates) under "Upcoming Anniversaries"
+ List formattedEntries = extractNameAndDateFromAnniversaryItems(entryCandidates);
+
+ // 7) Assert at least one entry is present and print them
+ Assert.assertTrue(formattedEntries.size() > 0, "Expected at least one upcoming anniversary entry");
+ String output = "Upcoming Anniversaries: [" + String.join(", ", formattedEntries) + "]";
+ System.out.println(output);
+
+ // 8) Clean up: log out
+ openUserDropdownIfPresent();
+ wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//a[normalize-space()='Logout']"))).click();
+
+ // Verify logout brings back login form
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("username")));
+ }
+
+ // Helper: Attempt to find each anniversary entry container in the panel
+ private List findAnniversaryEntryContainers(WebElement panelContainer) {
+ // Try common structures used in OrangeHRM sidebars
+ List items = new ArrayList<>();
+
+ // Most specific guess: employee details cards in the buzz sidebar
+ items.addAll(panelContainer.findElements(By.xpath(".//div[contains(@class,'employee-details') or contains(@class,'employee')][.//p]")));
+
+ // Fallbacks: list items or generic rows
+ if (items.isEmpty()) {
+ items.addAll(panelContainer.findElements(By.xpath(".//li[.//p]")));
+ }
+ if (items.isEmpty()) {
+ items.addAll(panelContainer.findElements(By.xpath(".//div[contains(@class,'list') or contains(@class,'item') or contains(@class,'row')][.//p]")));
+ }
+
+ // Filter out containers that don't seem like person rows (e.g., have no text p children)
+ return items.stream()
+ .filter(e -> !e.findElements(By.xpath(".//p[normalize-space() != '' and not(contains(.,'Upcoming Anniversaries'))]")).isEmpty())
+ .collect(Collectors.toList());
+ }
+
+ // Helper: Extract name and optional date from each entry container
+ private List extractNameAndDateFromAnniversaryItems(List entryContainers) {
+ List results = new ArrayList<>();
+ for (WebElement container : entryContainers) {
+ // Heuristic: The first p is often the name; second p may contain date or tenure info
+ String name = getTextOrEmpty(container, By.xpath(".//p[normalize-space()!=''][1]")).trim();
+
+ // Try to find a likely date or anniversary descriptor in the same container
+ String dateOrInfo = "";
+ List dateLocators = List.of(
+ By.xpath(".//p[contains(translate(.,'JFMASOND','jfmasond'),'jan') or contains(translate(.,'JFMASOND','jfmasond'),'feb') or contains(translate(.,'JFMASOND','jfmasond'),'mar') or contains(translate(.,'JFMASOND','jfmasond'),'apr') or contains(translate(.,'JFMASOND','jfmasond'),'may') or contains(translate(.,'JFMASOND','jfmasond'),'jun') or contains(translate(.,'JFMASOND','jfmasond'),'jul') or contains(translate(.,'JFMASOND','jfmasond'),'aug') or contains(translate(.,'JFMASOND','jfmasond'),'sep') or contains(translate(.,'JFMASOND','jfmasond'),'oct') or contains(translate(.,'JFMASOND','jfmasond'),'nov') or contains(translate(.,'JFMASOND','jfmasond'),'dec')]"),
+ By.xpath(".//p[contains(.,'-') or contains(.,'/') or contains(.,',')]"),
+ By.xpath(".//p[matches(., '.*[0-9].*')]") // will be ignored by drivers without XPath 2.0, but kept as last attempt
+ );
+
+ for (By locator : dateLocators) {
+ List candidates = container.findElements(locator);
+ if (!candidates.isEmpty()) {
+ // Choose the last texty element (often the most specific)
+ String candidate = candidates.get(candidates.size() - 1).getText().trim();
+ // Avoid accidentally picking the same as name
+ if (!candidate.equalsIgnoreCase(name) && candidate.length() >= 3) {
+ dateOrInfo = candidate;
+ break;
+ }
+ }
+ }
+
+ if (name.isEmpty()) {
+ // Skip empty/invalid rows
+ continue;
+ }
+
+ if (!dateOrInfo.isEmpty()) {
+ results.add(name + " - " + dateOrInfo);
+ } else {
+ results.add(name);
+ }
+ }
+ return results;
+ }
+
+ private void openUserDropdownIfPresent() {
+ // The user dropdown toggle can be a p.oxd-userdropdown-name or span.oxd-userdropdown-tab depending on build
+ By[] toggles = new By[] {
+ By.cssSelector("p.oxd-userdropdown-name"),
+ By.cssSelector("span.oxd-userdropdown-tab"),
+ By.xpath("//span[contains(@class,'oxd-userdropdown-tab') or contains(@class,'userdropdown')]")
+ };
+ for (By toggle : toggles) {
+ List els = driver.findElements(toggle);
+ if (!els.isEmpty()) {
+ try {
+ wait.until(ExpectedConditions.elementToBeClickable(els.get(0))).click();
+ return;
+ } catch (Exception ignored) { }
+ }
+ }
+ }
+
+ private void scrollIntoView(WebElement element) {
+ try {
+ ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({block:'center', inline:'nearest'});", element);
+ } catch (JavascriptException ignored) {
+ }
+ }
+
+ private void safeClick(WebElement element) {
+ try {
+ wait.until(ExpectedConditions.elementToBeClickable(element)).click();
+ } catch (Exception e) {
+ try {
+ new Actions(driver).moveToElement(element).click().perform();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ private void clickIfPresent(By locator) {
+ List elements = driver.findElements(locator);
+ if (!elements.isEmpty()) {
+ safeClick(elements.get(0));
+ }
+ }
+
+ private String getTextOrEmpty(WebElement scope, By locator) {
+ try {
+ WebElement el = scope.findElement(locator);
+ return el.getText() != null ? el.getText() : "";
+ } catch (NoSuchElementException e) {
+ return "";
+ }
+ }
+
+ private void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/java/OrangeHrmLoginAndVerifyPersonalInfoWithExtentTest.java b/ai-selenium-test-generator/src/test/java/OrangeHrmLoginAndVerifyPersonalInfoWithExtentTest.java
new file mode 100644
index 0000000..ee9969c
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/java/OrangeHrmLoginAndVerifyPersonalInfoWithExtentTest.java
@@ -0,0 +1,181 @@
+
+
+import com.aventstack.extentreports.ExtentReports;
+import com.aventstack.extentreports.ExtentTest;
+import com.aventstack.extentreports.MediaEntityBuilder;
+import com.aventstack.extentreports.reporter.ExtentSparkReporter;
+import org.openqa.selenium.By;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.Date;
+
+public class OrangeHrmLoginAndVerifyPersonalInfoWithExtentTest {
+
+ private WebDriver driver;
+ private WebDriverWait wait;
+ private ExtentReports extent;
+ private ExtentSparkReporter spark;
+ private ExtentTest test;
+
+ @BeforeClass
+ public void setUp() {
+ try {
+ Files.createDirectories(Paths.get("reports", "screenshots"));
+ } catch (IOException ignored) {
+ }
+ spark = new ExtentSparkReporter("reports/ExtentReport.html");
+ extent = new ExtentReports();
+ extent.attachReporter(spark);
+
+ driver = new ChromeDriver();
+ driver.manage().window().maximize();
+ wait = new WebDriverWait(driver, Duration.ofSeconds(20));
+ }
+
+ @AfterClass
+ public void tearDown() {
+ if (driver != null) {
+ driver.quit();
+ }
+ if (extent != null) {
+ extent.flush();
+ }
+ }
+
+ @Test(description = "Validate that a registered user can log in to the OrangeHRM demo site, navigate to My Info, and verify the Personal Details First Name is non-empty.")
+ public void testLoginAndVerifyPersonalInformationOnOrangeHRMDemo() throws IOException {
+ test = extent.createTest("Login and Verify Personal Information on OrangeHRM Demo");
+ String firstNameValue = null;
+
+ try {
+ // Step 1: Navigate to URL
+ String baseUrl = "https://opensource-demo.orangehrmlive.com/";
+ test.info("Step 1: Navigate to URL: " + baseUrl);
+ driver.get(baseUrl);
+ By usernameLocator = By.cssSelector("input[name='username']");
+ By passwordLocator = By.cssSelector("input[name='password']");
+ waitUntilVisible(usernameLocator);
+ waitUntilVisible(passwordLocator);
+ test.pass("Login page loads successfully (username and password fields are present).");
+
+ // Step 2: Verify page title
+ test.info("Step 2: Verify page title equals 'OrangeHRM'.");
+ wait.until(ExpectedConditions.titleContains("OrangeHRM"));
+ Assert.assertEquals(driver.getTitle(), "OrangeHRM", "Expected title to be 'OrangeHRM'.");
+ test.pass("Title equals 'OrangeHRM'.");
+
+ // Step 3: Enter Username
+ test.info("Step 3: Enter text into Username field: 'Admin'.");
+ WebElement usernameField = driver.findElement(usernameLocator);
+ usernameField.clear();
+ usernameField.sendKeys("Admin");
+ Assert.assertEquals(usernameField.getAttribute("value"), "Admin", "Username field does not contain 'Admin'.");
+ test.pass("Username field contains 'Admin'.");
+
+ // Step 4: Enter Password
+ test.info("Step 4: Enter text into Password field: 'admin123'.");
+ WebElement passwordField = driver.findElement(passwordLocator);
+ passwordField.clear();
+ passwordField.sendKeys("admin123");
+ Assert.assertTrue(passwordField.getAttribute("value").length() > 0, "Password field is empty.");
+ Assert.assertEquals(passwordField.getAttribute("type"), "password", "Password field is not masked.");
+ test.pass("Password field is populated and masked.");
+
+ // Step 5: Click Login button
+ test.info("Step 5: Click on Login button.");
+ By loginButton = By.cssSelector("button[type='submit']");
+ waitUntilClickable(loginButton).click();
+ wait.until(ExpectedConditions.or(
+ ExpectedConditions.urlContains("/dashboard"),
+ ExpectedConditions.presenceOfElementLocated(By.cssSelector("header h6"))
+ ));
+ Assert.assertFalse(driver.getCurrentUrl().contains("/auth/login"), "User is still on login page after attempting to login.");
+ test.pass("User is authenticated and navigated away from login page.");
+
+ // Step 6: Verify Dashboard header
+ test.info("Step 6: Verify 'Dashboard' header is visible.");
+ WebElement dashboardHeader = waitUntilVisible(By.cssSelector("header h6"));
+ Assert.assertEquals(dashboardHeader.getText().trim(), "Dashboard", "Dashboard header text mismatch.");
+ Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"), "URL does not contain '/dashboard'.");
+ test.pass("Dashboard page is displayed (header 'Dashboard' visible; URL contains '/dashboard').");
+
+ // Step 7: Click My Info
+ test.info("Step 7: Click sidebar menu item 'My Info'.");
+ By myInfoMenu = By.xpath("//span[normalize-space()='My Info']");
+ waitUntilClickable(myInfoMenu).click();
+ test.pass("Navigation to 'My Info' page initiated.");
+
+ // Step 8: Verify Personal Details header
+ test.info("Step 8: Verify 'Personal Details' section header is visible.");
+ WebElement personalDetailsHeader = waitUntilVisible(By.xpath("//h6[normalize-space()='Personal Details']"));
+ Assert.assertEquals(personalDetailsHeader.getText().trim(), "Personal Details", "Personal Details header not visible.");
+ test.pass("'Personal Details' section is displayed.");
+
+ // Step 9: Read First Name
+ test.info("Step 9: Read value from 'First Name' field.");
+ WebElement firstNameField = waitUntilVisible(By.cssSelector("input[name='firstName']"));
+ firstNameValue = firstNameField.getAttribute("value");
+ test.pass("Captured 'firstNameValue' successfully.");
+
+ // Step 10: Log First Name value
+ test.info("Step 10: Print/Log 'firstNameValue'.");
+ System.out.println("Captured First Name: " + firstNameValue);
+ test.pass("The value of 'firstNameValue' is: " + firstNameValue);
+
+ // Step 11: Assert Not Empty
+ test.info("Step 11: Assert that 'firstNameValue' is not empty.");
+ Assert.assertTrue(firstNameValue != null && firstNameValue.trim().length() > 0, "'firstNameValue' is empty.");
+ test.pass("Assertion passed: 'firstNameValue' is not empty.");
+
+ } catch (Exception e) {
+ String screenshotPath = captureScreenshot("failure");
+ test.fail("Test failed with exception: " + e.getMessage(),
+ MediaEntityBuilder.createScreenCaptureFromPath(screenshotPath).build());
+ Assert.fail("Test failed due to exception: " + e.getMessage(), e);
+ }
+ }
+
+ private WebElement waitUntilVisible(By locator) {
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
+ }
+
+ private WebElement waitUntilClickable(By locator) {
+ return wait.until(ExpectedConditions.elementToBeClickable(locator));
+ }
+
+ private String captureScreenshot(String namePrefix) {
+ String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS").format(new Date());
+ String fileName = namePrefix + "_" + timestamp + ".png";
+ Path destination = Paths.get("reports", "screenshots", fileName);
+ try {
+ Files.createDirectories(destination.getParent());
+ File src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
+ Files.copy(src.toPath(), destination, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException ioe) {
+ try {
+ byte[] bytes = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
+ Files.write(destination, bytes);
+ } catch (IOException ignored) {
+ }
+ }
+ return destination.toString();
+ }
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/resources/AI_Generated_JSON_Test.json b/ai-selenium-test-generator/src/test/resources/AI_Generated_JSON_Test.json
new file mode 100644
index 0000000..a1435ae
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/resources/AI_Generated_JSON_Test.json
@@ -0,0 +1,83 @@
+{
+ "testCaseName": "Login and Verify Personal Information on OrangeHRM Demo",
+ "description": "Validate that a registered user can log in to the OrangeHRM demo site, navigate to My Info, and verify that the Personal Details section displays a non-empty First Name.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Navigate to URL",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "input": null,
+ "expectedValidation": "Login page loads successfully (username and password fields are present)."
+ },
+ {
+ "stepNumber": 2,
+ "action": "Verify page title",
+ "target": "Browser Title",
+ "input": null,
+ "expectedValidation": "Title equals 'OrangeHRM'."
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter text",
+ "target": "Username field (CSS: input[name='username'])",
+ "input": "Admin",
+ "expectedValidation": "Username field contains 'Admin'."
+ },
+ {
+ "stepNumber": 4,
+ "action": "Enter text",
+ "target": "Password field (CSS: input[name='password'])",
+ "input": "admin123",
+ "expectedValidation": "Password field is populated (value masked)."
+ },
+ {
+ "stepNumber": 5,
+ "action": "Click",
+ "target": "Login button (CSS: button[type='submit'])",
+ "input": null,
+ "expectedValidation": "User is authenticated and navigated away from login page."
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify element visible",
+ "target": "Dashboard header (CSS: header h6, text equals 'Dashboard')",
+ "input": null,
+ "expectedValidation": "Dashboard page is displayed (header 'Dashboard' visible; URL may contain '/dashboard')."
+ },
+ {
+ "stepNumber": 7,
+ "action": "Click",
+ "target": "Sidebar menu item 'My Info' (e.g., XPath: //span[normalize-space()='My Info'])",
+ "input": null,
+ "expectedValidation": "Navigation to 'My Info' page initiated."
+ },
+ {
+ "stepNumber": 8,
+ "action": "Verify element visible",
+ "target": "Section header 'Personal Details' (CSS: h6, text equals 'Personal Details')",
+ "input": null,
+ "expectedValidation": "'Personal Details' section is displayed."
+ },
+ {
+ "stepNumber": 9,
+ "action": "Read/Get value",
+ "target": "First Name field (CSS: input[name='firstName'])",
+ "input": null,
+ "expectedValidation": "Capture the current value and store it as variable 'firstNameValue'."
+ },
+ {
+ "stepNumber": 10,
+ "action": "Print/Log",
+ "target": "Variable 'firstNameValue'",
+ "input": null,
+ "expectedValidation": "The value of 'firstNameValue' is printed to the console/log."
+ },
+ {
+ "stepNumber": 11,
+ "action": "Assert Not Empty",
+ "target": "Variable 'firstNameValue'",
+ "input": null,
+ "expectedValidation": "'firstNameValue' is not empty (length > 0)."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ai-selenium-test-generator/src/test/ressources/AI_GeneratedTest_20251114_200000.json b/ai-selenium-test-generator/src/test/ressources/AI_GeneratedTest_20251114_200000.json
new file mode 100644
index 0000000..4c961a6
--- /dev/null
+++ b/ai-selenium-test-generator/src/test/ressources/AI_GeneratedTest_20251114_200000.json
@@ -0,0 +1,172 @@
+[
+ {
+ "testCaseName": "Login - Valid Credentials (Verify Dashboard Visibility)",
+ "description": "Verify that a registered user can successfully log in to OrangeHRM using valid credentials and see the Dashboard.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Navigate",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputValues": null,
+ "expectedValidation": "Login page loads successfully."
+ },
+ {
+ "stepNumber": 2,
+ "action": "Verify Page Title",
+ "target": "Browser Title",
+ "inputValues": null,
+ "expectedValidation": "Title equals 'OrangeHRM'."
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter Text",
+ "target": "Username field",
+ "inputValues": {
+ "username": "Admin"
+ },
+ "expectedValidation": "Username value is entered."
+ },
+ {
+ "stepNumber": 4,
+ "action": "Enter Text",
+ "target": "Password field",
+ "inputValues": {
+ "password": "admin123"
+ },
+ "expectedValidation": "Password value is entered."
+ },
+ {
+ "stepNumber": 5,
+ "action": "Click",
+ "target": "Login button",
+ "inputValues": null,
+ "expectedValidation": "Login request is submitted."
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify Element Visibility",
+ "target": "Dashboard header",
+ "inputValues": null,
+ "expectedValidation": "Dashboard page is displayed (e.g., heading 'Dashboard' visible and/or URL contains '/dashboard')."
+ }
+ ]
+ },
+ {
+ "testCaseName": "Login - Valid Credentials (Verify URL and User Menu)",
+ "description": "Verify successful login with valid credentials by confirming Dashboard access via URL and presence of user profile/menu.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Navigate",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputValues": null,
+ "expectedValidation": "Login page loads successfully."
+ },
+ {
+ "stepNumber": 2,
+ "action": "Verify Page Title",
+ "target": "Browser Title",
+ "inputValues": null,
+ "expectedValidation": "Title equals 'OrangeHRM'."
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter Text",
+ "target": "Username field",
+ "inputValues": {
+ "username": "Admin"
+ },
+ "expectedValidation": "Username value is entered."
+ },
+ {
+ "stepNumber": 4,
+ "action": "Enter Text",
+ "target": "Password field",
+ "inputValues": {
+ "password": "admin123"
+ },
+ "expectedValidation": "Password value is entered."
+ },
+ {
+ "stepNumber": 5,
+ "action": "Click",
+ "target": "Login button",
+ "inputValues": null,
+ "expectedValidation": "Login request is submitted."
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify URL",
+ "target": "Browser Address Bar",
+ "inputValues": null,
+ "expectedValidation": "URL contains '/dashboard'."
+ },
+ {
+ "stepNumber": 7,
+ "action": "Verify Element Visibility",
+ "target": "User profile/avatar menu (top-right)",
+ "inputValues": null,
+ "expectedValidation": "User profile/avatar menu is visible, indicating authenticated session."
+ }
+ ]
+ },
+ {
+ "testCaseName": "Login - Invalid Credentials (Negative)",
+ "description": "Verify that login fails with an invalid password and the user remains on the login page with an appropriate error message.",
+ "steps": [
+ {
+ "stepNumber": 1,
+ "action": "Navigate",
+ "target": "https://opensource-demo.orangehrmlive.com/",
+ "inputValues": null,
+ "expectedValidation": "Login page loads successfully."
+ },
+ {
+ "stepNumber": 2,
+ "action": "Verify Page Title",
+ "target": "Browser Title",
+ "inputValues": null,
+ "expectedValidation": "Title equals 'OrangeHRM'."
+ },
+ {
+ "stepNumber": 3,
+ "action": "Enter Text",
+ "target": "Username field",
+ "inputValues": {
+ "username": "Admin"
+ },
+ "expectedValidation": "Username value is entered."
+ },
+ {
+ "stepNumber": 4,
+ "action": "Enter Text",
+ "target": "Password field",
+ "inputValues": {
+ "password": "wrongpass"
+ },
+ "expectedValidation": "Password value is entered."
+ },
+ {
+ "stepNumber": 5,
+ "action": "Click",
+ "target": "Login button",
+ "inputValues": null,
+ "expectedValidation": "Login request is submitted."
+ },
+ {
+ "stepNumber": 6,
+ "action": "Verify Error Message",
+ "target": "Login error notification/message",
+ "inputValues": null,
+ "expectedValidation": "Error message 'Invalid credentials' is displayed."
+ },
+ {
+ "stepNumber": 7,
+ "action": "Verify Stay on Page",
+ "target": "Login page",
+ "inputValues": null,
+ "expectedValidation": "Still on login page (e.g., URL contains '/auth' or login form visible). Dashboard is not visible."
+ }
+ ]
+ }
+]
\ No newline at end of file