From 656ee8313364a72c3aef552bea97b86f3624d891 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 13:30:00 -0400 Subject: [PATCH 1/7] Installed Selenium and Webdriver manager Co-authored-by: Copilot --- main.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index e6e4f28..7ba96cf 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,12 @@ from selenium import webdriver -from selenium.webdriver.common.by import Service +from webdriver_manager.chrome import ChromeDriverManager +from selenium.webdriver.chrome.service import Service -service = Service("chrome-win64/chromedriver.exe") \ No newline at end of file +service = Service(ChromeDriverManager().install()) +driver = webdriver.Chrome(service=service) + +driver.get("https://demoqa.com/login") + +input("Press Enter to close the browser...") + +driver.quit() \ No newline at end of file From 13edddad991fe26e7c464ada342ce1703f63fd57 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 14:13:44 -0400 Subject: [PATCH 2/7] Installed Webdriver_manager, and some selenum tools for locating and interactiong with web elements Co-authored-by: Copilot --- main.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 7ba96cf..3e68b41 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,35 @@ from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.by import By + + +chrome_options = Options() +chrome_options.add_argument("--disable-search-engine-choice-screen") service = Service(ChromeDriverManager().install()) -driver = webdriver.Chrome(service=service) +driver = webdriver.Chrome(options=chrome_options, service=service) driver.get("https://demoqa.com/login") +# Important elements are located using WebDriverWait to ensure they are visible before interacting with them +username_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) +password_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'password'))) +press_login = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'login'))) + + +username_field.send_keys("Pyman") +password_field.send_keys("pymaster123!") +press_login.click() +driver.implicitly_wait(5) # Wait for the page to load after clicking login + +if WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'name'))).text == "Invalid username or password!": + press_new_account = driver.find_element(By.ID, 'newUser') + driver.execute_script("arguments[0].click();", press_new_account) # Click "New User" using JavaScript + input("Press Enter to close the browser...") driver.quit() \ No newline at end of file From 41fd145ad58cb4bc3b83f3bba99fb157eee42df6 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 15:03:31 -0400 Subject: [PATCH 3/7] Added code to automate form input and submission Co-authored-by: Copilot --- main.py | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 3e68b41..29f8b45 100644 --- a/main.py +++ b/main.py @@ -22,13 +22,53 @@ username_field.send_keys("Pyman") -password_field.send_keys("pymaster123!") +password_field.send_keys("Pymaster123!") press_login.click() driver.implicitly_wait(5) # Wait for the page to load after clicking login -if WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'name'))).text == "Invalid username or password!": - press_new_account = driver.find_element(By.ID, 'newUser') - driver.execute_script("arguments[0].click();", press_new_account) # Click "New User" using JavaScript +# Check for the error message and click "New User" if the login fails + +# if WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'name'))).text == "Invalid username or password!": +# press_new_account = driver.find_element(By.ID, 'newUser') +# driver.execute_script("arguments[0].click();", press_new_account) # Click "New User" using JavaScript + +# # Fill out the form for creating a new user +# first_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'firstname'))) +# last_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'lastname'))) +# username_field_new = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) +# password_field_new = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'password'))) +# click_register = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'register'))) + + +# first_name_field.send_keys("Py") +# last_name_field.send_keys("Man") +# username_field_new.send_keys("Pyman") +# password_field_new.send_keys("Pymaster123!") + + + +#locate the elements dropdown and text box + +elements = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="root"]/div/div/div/div[1]/div/div/div[1]/span/div'))) +elements.click() + +text_box = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="item-0"]/a'))) +driver.execute_script("arguments[0].click();", text_box) # Click "Text Box" using JavaScript + +# Locate the form fields +full_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) +email_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="userEmail"]'))) +current_address_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="currentAddress"]'))) +permanent_address_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="permanentAddress"]'))) +submit_button = driver.find_element(By.XPATH, '//*[@id="submit"]') + + +# Fill out the form fields +full_name_field.send_keys("PyMan Smith") +email_field.send_keys("pyman@example.com") +current_address_field.send_keys("123 Main St, New York, NY 10001") +permanent_address_field.send_keys("456 Oak Ave, Los Angeles, CA 90210") +driver.execute_script("arguments[0].click();", submit_button) # Click "Submit" using JavaScript input("Press Enter to close the browser...") From a8cfa1905f44e5eb81e2f9300c59704666904675 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 15:15:48 -0400 Subject: [PATCH 4/7] comment refactor --- main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.py b/main.py index 29f8b45..4b19fc9 100644 --- a/main.py +++ b/main.py @@ -46,8 +46,7 @@ # password_field_new.send_keys("Pymaster123!") - -#locate the elements dropdown and text box +# locate the elements dropdown and text box elements = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="root"]/div/div/div/div[1]/div/div/div[1]/span/div'))) elements.click() @@ -62,7 +61,6 @@ permanent_address_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="permanentAddress"]'))) submit_button = driver.find_element(By.XPATH, '//*[@id="submit"]') - # Fill out the form fields full_name_field.send_keys("PyMan Smith") email_field.send_keys("pyman@example.com") From e1735ee8daa7c521b4e2a6bb9850b5e3f35f5f0e Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 17:11:12 -0400 Subject: [PATCH 5/7] Refactored code to follow OOP paradigm Co-authored-by: Copilot --- gui.py | 2 + main.py | 254 +++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 190 insertions(+), 66 deletions(-) create mode 100644 gui.py diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..66d29a8 --- /dev/null +++ b/gui.py @@ -0,0 +1,2 @@ +import tkinter + diff --git a/main.py b/main.py index 4b19fc9..3bc87e2 100644 --- a/main.py +++ b/main.py @@ -5,69 +5,191 @@ from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By - - -chrome_options = Options() -chrome_options.add_argument("--disable-search-engine-choice-screen") - -service = Service(ChromeDriverManager().install()) - -driver = webdriver.Chrome(options=chrome_options, service=service) -driver.get("https://demoqa.com/login") - -# Important elements are located using WebDriverWait to ensure they are visible before interacting with them -username_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) -password_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'password'))) -press_login = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'login'))) - - -username_field.send_keys("Pyman") -password_field.send_keys("Pymaster123!") -press_login.click() -driver.implicitly_wait(5) # Wait for the page to load after clicking login - -# Check for the error message and click "New User" if the login fails - -# if WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'name'))).text == "Invalid username or password!": -# press_new_account = driver.find_element(By.ID, 'newUser') -# driver.execute_script("arguments[0].click();", press_new_account) # Click "New User" using JavaScript - -# # Fill out the form for creating a new user -# first_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'firstname'))) -# last_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'lastname'))) -# username_field_new = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) -# password_field_new = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'password'))) -# click_register = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'register'))) - - -# first_name_field.send_keys("Py") -# last_name_field.send_keys("Man") -# username_field_new.send_keys("Pyman") -# password_field_new.send_keys("Pymaster123!") - - -# locate the elements dropdown and text box - -elements = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="root"]/div/div/div/div[1]/div/div/div[1]/span/div'))) -elements.click() - -text_box = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="item-0"]/a'))) -driver.execute_script("arguments[0].click();", text_box) # Click "Text Box" using JavaScript - -# Locate the form fields -full_name_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) -email_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="userEmail"]'))) -current_address_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="currentAddress"]'))) -permanent_address_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="permanentAddress"]'))) -submit_button = driver.find_element(By.XPATH, '//*[@id="submit"]') - -# Fill out the form fields -full_name_field.send_keys("PyMan Smith") -email_field.send_keys("pyman@example.com") -current_address_field.send_keys("123 Main St, New York, NY 10001") -permanent_address_field.send_keys("456 Oak Ave, Los Angeles, CA 90210") -driver.execute_script("arguments[0].click();", submit_button) # Click "Submit" using JavaScript - -input("Press Enter to close the browser...") - -driver.quit() \ No newline at end of file +import os +import json + +class WebAutomation: + def __init__(self): + chrome_options = Options() + chrome_options.add_argument("--disable-search-engine-choice-screen") + + download_path = os.getcwd() + prefs = { + "download.default_directory": download_path,} + chrome_options.add_experimental_option("prefs", prefs) + + + service = Service(ChromeDriverManager().install()) + + self.driver = webdriver.Chrome(options=chrome_options, service=service) + + def login(self, username, password): + self.driver.get("https://demoqa.com/login") + + # Important elements are located using WebDriverWait to ensure they are visible before interacting with them + username_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) + password_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'password'))) + press_login = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID, 'login'))) + + + username_field.send_keys(username) + password_field.send_keys(password) + press_login.click() + + + def complete_textbox(self, fullname, email, current_address, permanent_address): + elements = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="root"]/div/div/div/div[1]/div/div/div[1]/span/div'))) + elements.click() + + text_box = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="item-0"]/a'))) + self.driver.execute_script("arguments[0].click();", text_box) # Click "Text Box" using JavaScript + + # Locate the form fields + full_name_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'userName'))) + email_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="userEmail"]'))) + current_address_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="currentAddress"]'))) + permanent_address_field = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="permanentAddress"]'))) + submit_button = self.driver.find_element(By.XPATH, '//*[@id="submit"]') + + # Fill out the form fields and submit the form + full_name_field.send_keys(fullname) + email_field.send_keys(email) + current_address_field.send_keys(current_address) + permanent_address_field.send_keys(permanent_address) + self.driver.execute_script("arguments[0].click();", submit_button) # Click "Submit" using JavaScript + + def get_all_xpaths(self): + """Extract all XPaths and element info from the page""" + script = """ + function getElementXPath(element) { + if (element.id !== '') + return "//*[@id='" + element.id + "']"; + if (element === document.body) + return "//" + element.tagName.toLowerCase(); + + var ix = 0; + var siblings = element.parentNode.childNodes; + for (var i = 0; i < siblings.length; i++) { + var sibling = siblings[i]; + if (sibling === element) + return getElementXPath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix + 1) + ']'; + if (sibling.nodeType === 1 && sibling.tagName.toLowerCase() === element.tagName.toLowerCase()) + ix++; + } + } + + var elements = []; + document.querySelectorAll('*').forEach(function(el) { + elements.push({ + tag: el.tagName, + id: el.id || 'N/A', + class: el.className || 'N/A', + xpath: getElementXPath(el), + text: (el.innerText || '').substring(0, 50), + clickable: el.onclick !== null || el.tagName.match(/button|a|input/i) + }); + }); + return elements; + """ + + elements = self.driver.execute_script(script) + return elements + + def get_interactive_elements(self): + """Get only clickable/interactive elements (buttons, links, inputs)""" + script = """ + function getElementXPath(element) { + if (element.id !== '') + return "//*[@id='" + element.id + "']"; + if (element === document.body) + return "//" + element.tagName.toLowerCase(); + + var ix = 0; + var siblings = element.parentNode.childNodes; + for (var i = 0; i < siblings.length; i++) { + var sibling = siblings[i]; + if (sibling === element) + return getElementXPath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix + 1) + ']'; + if (sibling.nodeType === 1 && sibling.tagName.toLowerCase() === element.tagName.toLowerCase()) + ix++; + } + } + + var interactive = []; + document.querySelectorAll('button, a, input, select, textarea, [onclick]').forEach(function(el) { + interactive.push({ + tag: el.tagName, + id: el.id || 'N/A', + text: (el.innerText || el.value || el.placeholder || '').substring(0, 50), + xpath: getElementXPath(el) + }); + }); + return interactive; + """ + + return self.driver.execute_script(script) + + def save_xpaths_to_file(self, filename="xpaths.json", interactive_only=False): + """Save extracted XPaths to a JSON file""" + if interactive_only: + elements = self.get_interactive_elements() + else: + elements = self.get_all_xpaths() + + with open(filename, "w") as f: + json.dump(elements, f, indent=2) + + print(f"Saved {len(elements)} elements to {filename}") + return elements + + def check_box_list(self): + # Locate the Check box list element and click it + check_box_list = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="item-1"]/a'))) + self.driver.execute_script("arguments[0].click();", check_box_list) # Click "Check Box List" using JavaScript + + check_box = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.CLASS_NAME, 'rc-tree-checkbox'))) + self.driver.execute_script("arguments[0].click();", check_box) + + def radio_button_list(self): + # locate the next item in the list and click it + radio_button_list = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="item-2"]/a'))) + self.driver.execute_script("arguments[0].click();", radio_button_list) + + # locate the radio button and click it + radio_button = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'yesRadio'))) + self.driver.execute_script("arguments[0].click();", radio_button) + + # TODO [SCRUM-20]: add logic to export data from web tables into a xls file + def web_tables(self): + web_tables = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="item-3"]/a'))) + self.driver.execute_script("arguments[0].click();", web_tables) + + + def download(self): + # Locate the Download item and click it + download = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//*[@id="item-7"]/a'))) + self.driver.execute_script("arguments[0].click();", download) + + #click the button + download_button = WebDriverWait(self.driver, 10).until(EC.visibility_of_element_located((By.ID, 'downloadButton'))) + self.driver.execute_script("arguments[0].click();", download_button) + + def close(self): + self.driver.quit() + +web_automation = WebAutomation() + + + +if __name__ == "__main__": + # data for functions + web_automation.login("pyman", "Pymaster123!") + web_automation.save_xpaths_to_file("all_xpaths.json", interactive_only=False) + # Extract and save only interactive elements + web_automation.save_xpaths_to_file("interactive_elements.json", interactive_only=True) + web_automation.complete_textbox("PyMan Smith", "pyman@example.com", "123 Main St, New York, NY 10001", "456 Oak Ave, Los Angeles, CA 90210") + web_automation.check_box_list() + web_automation.radio_button_list() + web_automation.web_tables() + web_automation.download() + web_automation.close() From 8d14705efd4b6aa4f05d8b4bbd9bc279ac2f6020 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 17:29:10 -0400 Subject: [PATCH 6/7] new jira issues created --- gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gui.py b/gui.py index 66d29a8..7981981 100644 --- a/gui.py +++ b/gui.py @@ -1,2 +1,3 @@ import tkinter +#TODO [SCRUM-21] Create a dynamic gui that utilizes saved elements \ No newline at end of file From 8def77eaa037b642e61f773852f635fc1ca33370 Mon Sep 17 00:00:00 2001 From: Genbuos Date: Fri, 1 May 2026 18:17:58 -0400 Subject: [PATCH 7/7] GUI created Co-authored-by: Copilot --- __pycache__/main.cpython-313.pyc | Bin 0 -> 18240 bytes automation_config.json | 12 ++ gui.py | 322 ++++++++++++++++++++++++++++++- interactive_elements.json | 236 ++++++++++++++++++++++ main.py | 104 +++++++++- 5 files changed, 665 insertions(+), 9 deletions(-) create mode 100644 __pycache__/main.cpython-313.pyc create mode 100644 automation_config.json create mode 100644 interactive_elements.json diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b912e75683889b7747d01d5d758acbe0457902b4 GIT binary patch literal 18240 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", + lambda e: canvas.configure(scrollregion=canvas.bbox("all")) + ) + + canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + # Login Section + ttk.Label(scrollable_frame, text="Login Credentials", font=("Arial", 12, "bold")).pack(pady=10) + + ttk.Label(scrollable_frame, text="Username:").pack() + username_var = tk.StringVar(value=config_data["login"]["username"]) + ttk.Entry(scrollable_frame, textvariable=username_var, width=40).pack() + + ttk.Label(scrollable_frame, text="Password:").pack() + password_var = tk.StringVar(value=config_data["login"]["password"]) + ttk.Entry(scrollable_frame, textvariable=password_var, show="*", width=40).pack() + + # TextBox Section + ttk.Label(scrollable_frame, text="TextBox Form Data", font=("Arial", 12, "bold")).pack(pady=10) + + ttk.Label(scrollable_frame, text="Full Name:").pack() + fullname_var = tk.StringVar(value=config_data["textbox"]["fullname"]) + ttk.Entry(scrollable_frame, textvariable=fullname_var, width=40).pack() + + ttk.Label(scrollable_frame, text="Email:").pack() + email_var = tk.StringVar(value=config_data["textbox"]["email"]) + ttk.Entry(scrollable_frame, textvariable=email_var, width=40).pack() + + ttk.Label(scrollable_frame, text="Current Address:").pack() + current_addr_var = tk.StringVar(value=config_data["textbox"]["current_address"]) + ttk.Entry(scrollable_frame, textvariable=current_addr_var, width=40).pack() + + ttk.Label(scrollable_frame, text="Permanent Address:").pack() + permanent_addr_var = tk.StringVar(value=config_data["textbox"]["permanent_address"]) + ttk.Entry(scrollable_frame, textvariable=permanent_addr_var, width=40).pack() + + # Save button + def save_config(): + new_config = { + "login": { + "username": username_var.get(), + "password": password_var.get() + }, + "textbox": { + "fullname": fullname_var.get(), + "email": email_var.get(), + "current_address": current_addr_var.get(), + "permanent_address": permanent_addr_var.get() + } + } + + with open("automation_config.json", "w") as f: + json.dump(new_config, f, indent=2) + + messagebox.showinfo("Success", "Configuration saved to automation_config.json") + config_window.destroy() + + ttk.Button(scrollable_frame, text="Save Configuration", command=save_config).pack(pady=10) + + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + def run_automation(self): + """Run the full automation workflow using saved config""" + try: + # Load config + with open("automation_config.json", "r") as f: + config = json.load(f) + except FileNotFoundError: + messagebox.showerror("Error", "Config file not found. Click 'Configure Automation' first") + return + + try: + # Import WebAutomation from main.py + from main import WebAutomation + + # Create automation instance + automation = WebAutomation() + + # Run the workflow + messagebox.showinfo("Starting", "Automation workflow starting...") + + automation.login( + config["login"]["username"], + config["login"]["password"] + ) + messagebox.showinfo("Step 1", "Login complete") + + automation.complete_textbox( + config["textbox"]["fullname"], + config["textbox"]["email"], + config["textbox"]["current_address"], + config["textbox"]["permanent_address"] + ) + messagebox.showinfo("Step 2", "TextBox complete") + + automation.check_box_list() + messagebox.showinfo("Step 3", "CheckBox complete") + + automation.radio_button_list() + messagebox.showinfo("Step 4", "RadioButton complete") + + automation.web_tables() + messagebox.showinfo("Step 5", "WebTables complete") + + automation.download() + messagebox.showinfo("Step 6", "Download complete") + + automation.close() + messagebox.showinfo("Success", "Automation workflow completed!") + + except Exception as e: + messagebox.showerror("Error", f"Automation failed: {str(e)}") + +if __name__ == "__main__": + root = tk.Tk() + gui = AutomationGUI(root) + root.mainloop() diff --git a/interactive_elements.json b/interactive_elements.json new file mode 100644 index 0000000..146467d --- /dev/null +++ b/interactive_elements.json @@ -0,0 +1,236 @@ +[ + { + "id": "N/A", + "tag": "A", + "text": "", + "xpath": "//*[@id='root']/header[1]/a[1]" + }, + { + "id": "N/A", + "tag": "BUTTON", + "text": "", + "xpath": "//*[@id='root']/div[1]/div[1]/div[1]/div[1]/nav[1]/button[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Text Box", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Check Box", + "xpath": "//*[@id='item-1']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Radio Button", + "xpath": "//*[@id='item-2']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Web Tables", + "xpath": "//*[@id='item-3']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Buttons", + "xpath": "//*[@id='item-4']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Links", + "xpath": "//*[@id='item-5']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Broken Links - Images", + "xpath": "//*[@id='item-6']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Upload and Download", + "xpath": "//*[@id='item-7']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Dynamic Properties", + "xpath": "//*[@id='item-8']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Practice Form", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Browser Windows", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Alerts", + "xpath": "//*[@id='item-1']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Frames", + "xpath": "//*[@id='item-2']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Nested Frames", + "xpath": "//*[@id='item-3']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Modal Dialogs", + "xpath": "//*[@id='item-4']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Accordian", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Auto Complete", + "xpath": "//*[@id='item-1']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Date Picker", + "xpath": "//*[@id='item-2']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Slider", + "xpath": "//*[@id='item-3']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Progress Bar", + "xpath": "//*[@id='item-4']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Tabs", + "xpath": "//*[@id='item-5']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Tool Tips", + "xpath": "//*[@id='item-6']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Menu", + "xpath": "//*[@id='item-7']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Select Menu", + "xpath": "//*[@id='item-8']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Sortable", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Selectable", + "xpath": "//*[@id='item-1']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Resizable", + "xpath": "//*[@id='item-2']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Droppable", + "xpath": "//*[@id='item-3']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Dragabble", + "xpath": "//*[@id='item-4']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Login", + "xpath": "//*[@id='item-0']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Book Store", + "xpath": "//*[@id='item-2']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Profile", + "xpath": "//*[@id='item-3']/a[1]" + }, + { + "id": "N/A", + "tag": "A", + "text": "Book Store API", + "xpath": "//*[@id='item-4']/a[1]" + }, + { + "id": "userName", + "tag": "INPUT", + "text": "UserName", + "xpath": "//*[@id='userName']" + }, + { + "id": "password", + "tag": "INPUT", + "text": "Password", + "xpath": "//*[@id='password']" + }, + { + "id": "login", + "tag": "BUTTON", + "text": "Login", + "xpath": "//*[@id='login']" + }, + { + "id": "newUser", + "tag": "BUTTON", + "text": "New User", + "xpath": "//*[@id='newUser']" + } +] \ No newline at end of file diff --git a/main.py b/main.py index 3bc87e2..853bcd6 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,9 @@ from selenium.webdriver.common.by import By import os import json +import tkinter as tk +from tkinter import ttk, messagebox +import re class WebAutomation: def __init__(self): @@ -177,17 +180,104 @@ def download(self): def close(self): self.driver.quit() -web_automation = WebAutomation() +class ConfigurationTab: + def __init__(self, root): + self.frame = ttk.Frame(root) + self.config_data = {} + + # Login Section + ttk.Label(self.frame, text="Login Credentials", font=("Arial", 12, "bold")).pack(pady=10) + + ttk.Label(self.frame, text="Username:").pack() + self.username_var = tk.StringVar(value="pyman") + ttk.Entry(self.frame, textvariable=self.username_var, width=30).pack() + + ttk.Label(self.frame, text="Password:").pack() + self.password_var = tk.StringVar(value="Pymaster123!") + ttk.Entry(self.frame, textvariable=self.password_var, show="*", width=30).pack() + + # TextBox Section + ttk.Label(self.frame, text="TextBox Form Data", font=("Arial", 12, "bold")).pack(pady=10) + + ttk.Label(self.frame, text="Full Name:").pack() + self.fullname_var = tk.StringVar(value="PyMan Smith") + ttk.Entry(self.frame, textvariable=self.fullname_var, width=30).pack() + + ttk.Label(self.frame, text="Email:").pack() + self.email_var = tk.StringVar(value="pyman@example.com") + ttk.Entry(self.frame, textvariable=self.email_var, width=30).pack() + + ttk.Label(self.frame, text="Current Address:").pack() + self.current_addr_var = tk.StringVar(value="123 Main St, New York, NY 10001") + ttk.Entry(self.frame, textvariable=self.current_addr_var, width=30).pack() + + ttk.Label(self.frame, text="Permanent Address:").pack() + self.permanent_addr_var = tk.StringVar(value="456 Oak Ave, Los Angeles, CA 90210") + ttk.Entry(self.frame, textvariable=self.permanent_addr_var, width=30).pack() + + # Save button + ttk.Button(self.frame, text="Save Configuration", command=self.save_config).pack(pady=10) + + self.status_label = ttk.Label(self.frame, text="", foreground="green") + self.status_label.pack() + + def save_config(self): + """Save configuration to JSON file""" + self.config_data = { + "login": { + "username": self.username_var.get(), + "password": self.password_var.get() + }, + "textbox": { + "fullname": self.fullname_var.get(), + "email": self.email_var.get(), + "current_address": self.current_addr_var.get(), + "permanent_address": self.permanent_addr_var.get() + } + } + + with open("automation_config.json", "w") as f: + json.dump(self.config_data, f, indent=2) + + self.status_label.config(text="Configuration saved successfully!") + messagebox.showinfo("Success", "Configuration saved to automation_config.json") + +def load_config(filename="automation_config.json"): + """Load automation configuration from JSON file""" + try: + with open(filename, "r") as f: + return json.load(f) + except FileNotFoundError: + print(f"Config file {filename} not found. Using defaults.") + return { + "login": {"username": "pyman", "password": "Pymaster123!"}, + "textbox": { + "fullname": "PyMan Smith", + "email": "pyman@example.com", + "current_address": "123 Main St, New York, NY 10001", + "permanent_address": "456 Oak Ave, Los Angeles, CA 90210" + } + } + + + if __name__ == "__main__": - # data for functions - web_automation.login("pyman", "Pymaster123!") - web_automation.save_xpaths_to_file("all_xpaths.json", interactive_only=False) - # Extract and save only interactive elements - web_automation.save_xpaths_to_file("interactive_elements.json", interactive_only=True) - web_automation.complete_textbox("PyMan Smith", "pyman@example.com", "123 Main St, New York, NY 10001", "456 Oak Ave, Los Angeles, CA 90210") + config = load_config() + + web_automation = WebAutomation() + web_automation.login( + config["login"]["username"], + config["login"]["password"] + ) + web_automation.complete_textbox( + config["textbox"]["fullname"], + config["textbox"]["email"], + config["textbox"]["current_address"], + config["textbox"]["permanent_address"] + ) web_automation.check_box_list() web_automation.radio_button_list() web_automation.web_tables()