Skip to content

Commit 27ef055

Browse files
AnshulAnshul
authored andcommitted
Selenium AI
1 parent eb1469d commit 27ef055

10 files changed

Lines changed: 431 additions & 0 deletions

File tree

.DS_Store

0 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
OPENAI_API_KEY=
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import org.openqa.selenium.By;
2+
import org.openqa.selenium.WebDriver;
3+
import org.openqa.selenium.WebElement;
4+
import org.openqa.selenium.chrome.ChromeDriver;
5+
import org.openqa.selenium.chrome.ChromeOptions;
6+
import org.openqa.selenium.support.ui.ExpectedConditions;
7+
import org.openqa.selenium.support.ui.WebDriverWait;
8+
import org.testng.Assert;
9+
import org.testng.annotations.AfterClass;
10+
import org.testng.annotations.BeforeClass;
11+
import org.testng.annotations.Test;
12+
13+
import java.time.Duration;
14+
15+
public class OrangeHRMLoginTest {
16+
17+
private WebDriver driver;
18+
private WebDriverWait wait;
19+
20+
private static final String BASE_URL = "https://opensource-demo.orangehrmlive.com/";
21+
private static final String VALID_USERNAME = "Admin";
22+
private static final String VALID_PASSWORD = "admin123";
23+
private static final String EXPECTED_LOGIN_TITLE = "OrangeHRM";
24+
25+
// Locators
26+
private static final By USERNAME_INPUT = By.name("username");
27+
private static final By PASSWORD_INPUT = By.name("password");
28+
private static final By LOGIN_BUTTON = By.cssSelector("button[type='submit']");
29+
private static final By DASHBOARD_HEADER = By.cssSelector("h6.oxd-text.oxd-text--h6.oxd-topbar-header-breadcrumb-module");
30+
31+
@BeforeClass(alwaysRun = true)
32+
public void setUp() {
33+
// Selenium 4.6+ will manage the ChromeDriver binary automatically
34+
ChromeOptions options = new ChromeOptions();
35+
// options.addArguments("--headless=new"); // Uncomment for headless runs in CI
36+
driver = new ChromeDriver(options);
37+
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
38+
driver.manage().window().maximize();
39+
}
40+
41+
@AfterClass(alwaysRun = true)
42+
public void tearDown() {
43+
if (driver != null) {
44+
driver.quit();
45+
}
46+
}
47+
48+
@Test(description = "As a registered user, I can log in with valid credentials and access the dashboard")
49+
public void userCanLoginWithValidCredentials() {
50+
// Navigate to the application
51+
driver.get(BASE_URL);
52+
53+
// Ensure login page is loaded
54+
waitForVisible(USERNAME_INPUT);
55+
56+
// Verify the Page Title
57+
String actualLoginTitle = driver.getTitle();
58+
Assert.assertEquals(actualLoginTitle, EXPECTED_LOGIN_TITLE, "Login page title should match expected.");
59+
60+
// Enter valid username and password and submit
61+
login(VALID_USERNAME, VALID_PASSWORD);
62+
63+
// Verify successful login by checking that the Dashboard is visible
64+
WebElement dashboardHeading = waitForVisible(DASHBOARD_HEADER);
65+
Assert.assertTrue(dashboardHeading.isDisplayed(), "Dashboard heading should be visible after login.");
66+
Assert.assertEquals(dashboardHeading.getText().trim(), "Dashboard", "User should land on the Dashboard.");
67+
68+
// Optional: Also verify URL contains 'dashboard' and title remains consistent
69+
Assert.assertTrue(driver.getCurrentUrl().toLowerCase().contains("dashboard"),
70+
"Current URL should contain 'dashboard' after login.");
71+
Assert.assertEquals(driver.getTitle(), EXPECTED_LOGIN_TITLE, "Page title remains consistent after login.");
72+
}
73+
74+
// Helper methods
75+
76+
private void login(String username, String password) {
77+
WebElement usernameField = waitForVisible(USERNAME_INPUT);
78+
WebElement passwordField = waitForVisible(PASSWORD_INPUT);
79+
usernameField.clear();
80+
usernameField.sendKeys(username);
81+
passwordField.clear();
82+
passwordField.sendKeys(password);
83+
driver.findElement(LOGIN_BUTTON).click();
84+
}
85+
86+
private WebElement waitForVisible(By locator) {
87+
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
88+
}
89+
}

ai-selenium-test-generator/pom.xml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>ai-selenium</groupId>
6+
<artifactId>ai-selenium-test-generator</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
9+
<dependencies>
10+
<dependency>
11+
<groupId>com.openai</groupId>
12+
<artifactId>openai-java</artifactId>
13+
<version>4.6.1</version>
14+
</dependency>
15+
16+
<!--
17+
https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
18+
<dependency>
19+
<groupId>org.seleniumhq.selenium</groupId>
20+
<artifactId>selenium-java</artifactId>
21+
<version>4.28.1</version>
22+
</dependency>
23+
24+
<!-- https://mvnrepository.com/artifact/org.json/json -->
25+
<dependency>
26+
<groupId>org.json</groupId>
27+
<artifactId>json</artifactId>
28+
<version>20250517</version>
29+
</dependency>
30+
31+
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
32+
<dependency>
33+
<groupId>org.testng</groupId>
34+
<artifactId>testng</artifactId>
35+
<version>7.11.0</version>
36+
<scope>test</scope>
37+
</dependency>
38+
39+
</dependencies>
40+
41+
</project>
42+
43+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import java.io.FileInputStream;
2+
import java.io.IOException;
3+
import java.util.Properties;
4+
5+
public class Config {
6+
public static String getApiKey() {
7+
try {
8+
Properties props = new Properties();
9+
props.load(new FileInputStream("config.properties"));
10+
return props.getProperty("OPENAI_API_KEY");
11+
} catch (IOException e) {
12+
throw new RuntimeException("Failed to read API key from config file", e);
13+
}
14+
}
15+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import java.time.LocalDateTime;
2+
import java.time.format.DateTimeFormatter;
3+
4+
public class GenerateTestFromStory {
5+
6+
public static void main(String[] args) {
7+
8+
String userStory = """
9+
As a registered user,
10+
I want to log in to the website with valid credentials
11+
so that I can access my dashboard.
12+
13+
Acceptance Criteria:
14+
- Navigate to https://opensource-demo.orangehrmlive.com/
15+
- Verify the Page Title
16+
- Enter valid username and password
17+
- Verify successful login by checking dashboard visibility or Page Title
18+
""";
19+
20+
try {
21+
System.out.println("Sending user story to AI...");
22+
String generatedCode = OpenAIClientWrapper.generateCodeFromStory(userStory);
23+
System.out.println("\nAI Generated Test Code:\n");
24+
//System.out.println(generatedCode);
25+
26+
// build timestamped filename and save
27+
String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
28+
String fileName = "generated-tests/AI_GeneratedTest_" + ts + ".java";
29+
OpenAIClientWrapper.saveToFile(generatedCode, fileName);
30+
System.out.println("\nSaved generated File: " + fileName);
31+
32+
} catch (Exception e){
33+
e.printStackTrace();
34+
}
35+
}
36+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import com.openai.client.OpenAIClient;
2+
import com.openai.client.okhttp.OpenAIOkHttpClient;
3+
import com.openai.models.responses.Response;
4+
import com.openai.models.responses.ResponseCreateParams;
5+
6+
public class Main {
7+
public static void main(String[] args) {
8+
9+
//System.out.println("Key: " + System.getenv("OPENAI_API_KEY"));
10+
11+
// Create client from environment variables
12+
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
13+
14+
ResponseCreateParams params = ResponseCreateParams.builder()
15+
.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")
16+
.model("gpt-5")
17+
.build();
18+
19+
Response response = client.responses().create(params);
20+
System.out.println(response.output());
21+
}
22+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import java.util.logging.Logger;
2+
import java.io.IOException;
3+
import java.nio.file.Files;
4+
import java.nio.file.Path;
5+
import java.nio.file.StandardOpenOption;
6+
7+
import com.openai.client.OpenAIClient;
8+
import com.openai.client.okhttp.OpenAIOkHttpClient;
9+
import com.openai.models.chat.completions.ChatCompletion;
10+
import com.openai.models.chat.completions.ChatCompletionCreateParams;
11+
import com.openai.models.ChatModel;
12+
13+
/**
14+
*
15+
* This class connects to the OpenAI API to generate Selenium + TestNG Java test
16+
* cases automatically from user stories or requirements.
17+
*/
18+
19+
public class OpenAIClientWrapper {
20+
private static final Logger LOGGER = Logger.getLogger(OpenAIClientWrapper.class.getName());
21+
private static OpenAIClient client;
22+
23+
public OpenAIClientWrapper() {
24+
String apiKey = Config.getApiKey();
25+
if (apiKey == null || apiKey.isBlank()) {
26+
throw new IllegalArgumentException("API key must not be blank");
27+
}
28+
this.client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
29+
System.out.println(client);
30+
}
31+
32+
private static void initClientIfNeeded() {
33+
if (client == null) {
34+
synchronized (OpenAIClientWrapper.class) {
35+
if (client == null) {
36+
String apiKey = Config.getApiKey();
37+
if (apiKey == null || apiKey.isBlank()) {
38+
throw new IllegalArgumentException("API key must not be blank");
39+
}
40+
// This is compatible with openai-java 4.6.1 style you used earlier
41+
client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
42+
LOGGER.info("OpenAI client initialized.");
43+
}
44+
}
45+
}
46+
}
47+
48+
// Generates Java source code for a Selenium + TestNG test based on the given
49+
// user story.
50+
public static String generateCodeFromStory(String userStory) throws IOException {
51+
if (userStory == null || userStory.isBlank()) {
52+
throw new IllegalArgumentException("userStory must not be blank");
53+
}
54+
55+
initClientIfNeeded();
56+
57+
// Construct the system prompt
58+
String systemPrompt = """
59+
You are an expert Test Automation Engineer. Generate a clean, maintainable Selenium + TestNG test in Java.
60+
Provide full Java source code with imports, class, method, ChromeDriver setup and teardown, descriptive naming, and assertions.
61+
""";
62+
63+
// Create chat completion parameters
64+
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder().model(ChatModel.GPT_5)
65+
.addSystemMessage(systemPrompt).addUserMessage("User Story / Acceptance Criteria:\n\n" + userStory)
66+
.build();
67+
68+
// Call the API
69+
ChatCompletion result = client.chat().completions().create(params);
70+
71+
// Extract content
72+
String code = result.choices().get(0).message().content()
73+
.orElseThrow(() -> new IOException("No content in OpenAI response"));
74+
75+
return code.trim();
76+
}
77+
78+
// Saves the generated code to a .java file.
79+
public static void saveToFile(String code, String fileName) throws IOException {
80+
if (code == null || code.isBlank()) {
81+
throw new IllegalArgumentException("code must not be blank");
82+
}
83+
if (fileName == null || fileName.isBlank()) {
84+
throw new IllegalArgumentException("fileName must not be blank");
85+
}
86+
87+
Path path = Path.of(fileName).toAbsolutePath();
88+
89+
// Ensure parent directories exist
90+
Path parent = path.getParent();
91+
if (parent != null && !Files.exists(parent)) {
92+
Files.createDirectories(parent);
93+
}
94+
95+
Files.writeString(path, code, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
96+
LOGGER.info("Saved generated code to: " + path);
97+
}
98+
99+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import com.openai.client.OpenAIClient;
2+
import com.openai.client.okhttp.OpenAIOkHttpClient;
3+
import com.openai.models.responses.Response;
4+
import com.openai.models.responses.ResponseCreateParams;
5+
import java.util.List;
6+
7+
public class SampleTestOpenAI {
8+
9+
public static void main(String[] args) {
10+
String apiKey = Config.getApiKey();
11+
OpenAIClient client = OpenAIOkHttpClient.builder().apiKey(apiKey).build();
12+
13+
// Example user story to generate test cases from
14+
String userStory = "As a registered user, I want to reset my password so that I can regain access if I forget it.";
15+
16+
// Build prompt: be explicit about required output format
17+
String prompt = "You are a software QA engineer. Given the following user story, generate 3 test cases in Gherkin format (Given/When/Then).\n"
18+
+ "Provide each test case with:\n" + "- Title\n" + "- Preconditions\n"
19+
+ "- Steps in Gherkin (Given/When/Then)\n" + "- Expected result summary\n\n" + "User story:\n"
20+
+ userStory
21+
+ "\n\nRespond only with a JSON array of objects with fields: title, preconditions, gherkin, expected.\n";
22+
23+
ResponseCreateParams params = ResponseCreateParams.builder()
24+
.input(prompt).model("gpt-4.1").build();
25+
26+
try {
27+
Response response = client.responses().create(params);
28+
// The SDK returns a Response object — print the model output(s)
29+
System.out.println("Raw response object: " + response);
30+
} catch (Exception e) {
31+
System.err.println("Error calling OpenAI: " + e.getMessage());
32+
e.printStackTrace();
33+
}
34+
35+
}
36+
37+
}

0 commit comments

Comments
 (0)