diff --git a/README.md b/README.md index f6b37ae..04be85f 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ -# selenium \ No newline at end of file +# ๐Ÿงช Automated Web Testing with Selenium + +> **Group 79** | Institute of Engineering & Management (IEM), New Town +> **Course:** Selenium Web Automation + +--- + +## ๐Ÿ‘ฅ Contributors + +| Student Name | University Roll | Enrollment ID | Stream | Section | +| :--- | :---: | :---: | :---: | :---: | +| **Yashraj Sharma** | 04 | `12023002029115` | CSE (IoT, CS & BT) | C | +| **Suvajit Majhi** | 56 | `12023002029105` | CSE (IoT, CS & BT) | B | +| **Tanisha Pan** | 62 | `12023002029111` | CSE (IoT, CS & BT) | B | + +--- + +## ๐Ÿš€ Lab Modules + +### ๐Ÿ”น Module 1: Foundational DOM Locators +* **Summary:** Implemented the 5 primary locators (`By.ID`, `By.NAME`, `By.TAG_NAME`, `By.LINK_TEXT`, `By.CLASS_NAME`) to fill inputs, select radio buttons, read headers, and click links on `testautomationpractice.blogspot.com`. +* **Demo:** [๐ŸŽฅ Watch Recording](https://drive.google.com/file/d/1ea_nhYImdo8orjf-AMOG2eH3wglGtPEW/view?usp=sharing) + +--- + +### ๐Ÿ”น Module 2: Bulk Element Extraction +* **Summary:** Used `find_elements()` with `By.TAG_NAME` to query all anchor tags (``) on `testautomationpractice.blogspot.com`, tallying the total count and printing their visible text. +* **Demo:** [๐ŸŽฅ Watch Recording](https://drive.google.com/file/d/12ioBdbEPhWcfHC4uHiXMQQRzg9gQJMeZ/view?usp=sharing) + +--- + +### ๐Ÿ”น Module 3: Pattern Matching with CSS Wildcards +* **Summary:** Handled dynamic checkboxes and radio buttons on `rahulshettyacademy.com/AutomationPractice` using CSS substring matchers (`^=`, `*=`, `$=`). +* **Demo:** [๐ŸŽฅ Watch Recording](https://drive.google.com/file/d/1xsArkTYS268ECMxdnxLvo5uK9PVpSqJI/view?usp=sharing) + +--- + +### ๐Ÿ”น Module 4: DOM Traversal via Child Selectors +* **Summary:** Targeted deeply nested form controls and table rows on practice portals using explicit CSS child hierarchies (`fieldset > label > input`, `table > tbody > tr`). +* **Demo:** [๐ŸŽฅ Watch Recording](https://drive.google.com/file/d/1g5hsRkwEvTH64a_M6j2s53kI78cPT1uq/view?usp=sharing) + +--- + +## ๐Ÿ“Œ Notes + +* Video demonstrations are hosted on Google Drive with public view access enabled. + +
+ โœจ Selenium Web Automation โ€” Group 79 โœจ +
diff --git a/submission_tanisha/Assignment 1.py b/submission_tanisha/Assignment 1.py new file mode 100644 index 0000000..99e50e4 --- /dev/null +++ b/submission_tanisha/Assignment 1.py @@ -0,0 +1,160 @@ +import sys +import time +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.firefox.options import Options as FirefoxOptions +from selenium.webdriver.firefox.service import Service as FirefoxService + +# Optional fallback support for webdriver-manager if installed +try: + from webdriver_manager.firefox import GeckoDriverManager + WEBDRIVER_MANAGER_AVAILABLE = True +except ImportError: + WEBDRIVER_MANAGER_AVAILABLE = False + + +def initialize_firefox(): + """ + Initializes and returns a Firefox WebDriver instance. + Optimized for Arch Linux: + - Uses native geckodriver / Selenium Manager by default. + - Falls back to webdriver-manager if present. + """ + options = FirefoxOptions() + # options.add_argument("--headless") # Uncomment if headless mode is required + + try: + # Selenium 4.6+ automatically discovers geckodriver from system PATH + return webdriver.Firefox(options=options) + except Exception as primary_err: + if WEBDRIVER_MANAGER_AVAILABLE: + try: + service = FirefoxService(GeckoDriverManager().install()) + return webdriver.Firefox(service=service, options=options) + except Exception as fallback_err: + raise RuntimeError( + f"Failed to launch Firefox via webdriver-manager: {fallback_err}" + ) from primary_err + raise RuntimeError( + "Could not launch Firefox WebDriver. On Arch Linux, install geckodriver via:\n" + " sudo pacman -S firefox geckodriver\n" + f"Original error: {primary_err}" + ) + + +driver = None + +try: + print("=" * 65) + print(" SELENIUM AUTOMATION: ASSIGNMENT 1 (LOCATOR IDENTIFICATION)") + print("=" * 65) + print("Launching Firefox browser on Arch Linux...") + + driver = initialize_firefox() + + # Step 1: Open target practice website + target_url = "https://testautomationpractice.blogspot.com/" + print(f"Navigating to: {target_url}") + driver.get(target_url) + driver.maximize_window() + time.sleep(3) # Short pause to ensure DOM tree is fully parsed + + # ========================================================================= + # 1. LOCATE BY ID (By.ID) + # Target: Name input text box + # ========================================================================= + print("\n" + "-" * 65) + print("1. LOCATING ELEMENT BY ID (By.ID)") + print("-" * 65) + + name_field = driver.find_element(By.ID, "name") + print(f"Element found using ID: '{name_field.get_attribute('id')}'") + name_field.clear() + name_field.send_keys("Suvajit") + print("Action performed: Entered text 'Suvajit' into name field.") + time.sleep(2) + + # ========================================================================= + # 2. LOCATE BY NAME (By.NAME) + # Target: Gender radio button + # ========================================================================= + print("\n" + "-" * 65) + print("2. LOCATING ELEMENT BY NAME (By.NAME)") + print("-" * 65) + + gender_radio = driver.find_element(By.NAME, "gender") + print(f"Element found using NAME: '{gender_radio.get_attribute('name')}'") + gender_radio.click() + print("Action performed: Clicked gender radio button.") + time.sleep(2) + + # ========================================================================= + # 3. LOCATE BY TAG NAME (By.TAG_NAME) + # Target: Main heading

+ # ========================================================================= + print("\n" + "-" * 65) + print("3. LOCATING ELEMENT BY TAG NAME (By.TAG_NAME)") + print("-" * 65) + + heading_element = driver.find_element(By.TAG_NAME, "h1") + print(f"Heading Tag Text: \"{heading_element.text.strip()}\"") + time.sleep(2) + + # ========================================================================= + # 4. LOCATE BY LINK TEXT (By.LINK_TEXT) + # Target: Anchor link with exact matching visible text + # ========================================================================= + print("\n" + "-" * 65) + print("4. LOCATING ELEMENT BY LINK TEXT (By.LINK_TEXT)") + print("-" * 65) + + # Try locating "Apple" or fallback to available navbar link if not found + try: + link_element = driver.find_element(By.LINK_TEXT, "Apple") + print(f"Link found with text: \"{link_element.text}\"") + print(f"Target URL (href): {link_element.get_attribute('href')}") + except Exception: + # Fallback to any visible anchor link (e.g. 'merrymoonmary' / 'Home' / 'GUI Elements') + fallback_link = driver.find_element(By.PARTIAL_LINK_TEXT, "open cart") + print(f"Link found with text: \"{fallback_link.text}\"") + print(f"Target URL (href): {fallback_link.get_attribute('href')}") + time.sleep(2) + + # ========================================================================= + # 5. LOCATE BY CLASS NAME (By.CLASS_NAME) + # Target: Form control element + # ========================================================================= + print("\n" + "-" * 65) + print("5. LOCATING ELEMENT BY CLASS NAME (By.CLASS_NAME)") + print("-" * 65) + + form_element = driver.find_element(By.CLASS_NAME, "form-control") + print(f"Class attribute value: '{form_element.get_attribute('class')}'") + print(f"Tag Name: <{form_element.tag_name}> | Element ID: '{form_element.get_attribute('id')}'") + time.sleep(2) + + # ========================================================================= + # ASSIGNMENT 1 SUMMARY + # ========================================================================= + print("\n" + "=" * 65) + print("ASSIGNMENT 1 COMPLETED SUCCESSFULLY") + print("=" * 65) + print("Locators demonstrated:") + print(" 1. By.ID -> Identified element by unique 'id' attribute") + print(" 2. By.NAME -> Identified form element by 'name' attribute") + print(" 3. By.TAG_NAME -> Identified element by HTML tag (

)") + print(" 4. By.LINK_TEXT -> Identified hyperlink by exact visible text") + print(" 5. By.CLASS_NAME -> Identified element matching CSS class name") + print("=" * 65) + + # Keep browser open for visual review + input("\nPress ENTER in your terminal to close Firefox and exit...") + +except Exception as error: + print(f"\n[Execution Error]: {error}", file=sys.stderr) + +finally: + if driver is not None: + print("\nClosing Firefox browser session...") + driver.quit() + print("Firefox closed successfully.") diff --git a/submission_tanisha/Assignment 2.py b/submission_tanisha/Assignment 2.py new file mode 100644 index 0000000..8a3e999 --- /dev/null +++ b/submission_tanisha/Assignment 2.py @@ -0,0 +1,121 @@ +import sys +import time +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.firefox.options import Options as FirefoxOptions +from selenium.webdriver.firefox.service import Service as FirefoxService + +# Optional fallback for webdriver-manager if installed +try: + from webdriver_manager.firefox import GeckoDriverManager + WEBDRIVER_MANAGER_AVAILABLE = True +except ImportError: + WEBDRIVER_MANAGER_AVAILABLE = False + + +def initialize_firefox(): + """ + Initializes and returns a Firefox WebDriver instance. + Optimized for Arch Linux: + - Uses geckodriver from system PATH / Selenium Manager. + - Falls back to webdriver-manager if available. + """ + options = FirefoxOptions() + # options.add_argument("--headless") # Uncomment if headless execution is needed + + try: + # Selenium 4.6+ discovers geckodriver on Arch automatically + return webdriver.Firefox(options=options) + except Exception as primary_err: + if WEBDRIVER_MANAGER_AVAILABLE: + try: + service = FirefoxService(GeckoDriverManager().install()) + return webdriver.Firefox(service=service, options=options) + except Exception as fallback_err: + raise RuntimeError( + f"Failed to launch Firefox via webdriver-manager: {fallback_err}" + ) from primary_err + raise RuntimeError( + "Could not launch Firefox WebDriver. On Arch Linux, install geckodriver via:\n" + " sudo pacman -S firefox geckodriver\n" + f"Original error: {primary_err}" + ) + + +driver = None + +try: + print("=" * 60) + print(" SELENIUM AUTOMATION: ASSIGNMENT 2 (MULTIPLE ELEMENTS)") + print("=" * 60) + print("Launching Firefox browser on Arch Linux...") + + driver = initialize_firefox() + + # Step 1: Open the target practice website + target_url = "https://testautomationpractice.blogspot.com/" + print(f"Navigating to: {target_url}") + driver.get(target_url) + driver.maximize_window() + time.sleep(3) # Short pause to ensure DOM tree is fully parsed + + # ========================================================================= + # MULTIPLE ELEMENT IDENTIFICATION: FIND ALL LINKS + # find_elements() returns a Python list of all matching WebElements + # ========================================================================= + print("\n" + "-" * 60) + print("1. FINDING ALL HYPERLINKS ( tags)") + print("-" * 60) + + all_links = driver.find_elements(By.TAG_NAME, "a") + + # Display total count using len() + print(f"Total number of links found on the page: {len(all_links)}\n") + time.sleep(1) + + # ========================================================================= + # ITERATING THROUGH THE LIST OF ELEMENTS + # ========================================================================= + print("List of visible links on the webpage:") + print("-" * 60) + + count = 1 + for link in all_links: + try: + link_text = link.text.strip() + + # Filter and print only links with visible text + if link_text: + print(f"{count:>3}. {link_text}") + count += 1 + except Exception as elem_err: + # Handle any stale elements gracefully + continue + + time.sleep(1) + + # ========================================================================= + # ASSIGNMENT SUMMARY + # ========================================================================= + print("\n" + "=" * 60) + print("ASSIGNMENT 2 COMPLETED SUCCESSFULLY") + print("=" * 60) + print("Concepts demonstrated:") + print(" 1. driver.find_elements(By.TAG_NAME, 'a') -> Returns list of WebElements") + print(" 2. len(all_links) -> Gets total count of elements") + print(" 3. for link in all_links: -> Iterates over the list") + print(" 4. link.text -> Reads visible element text") + print("=" * 60) + + # Keep browser open for inspection + input("\nPress ENTER in your terminal to close Firefox and exit...") + +except Exception as error: + print(f"\n[Execution Error]: {error}", file=sys.stderr) + +finally: + # Safely close the browser session + if driver is not None: + print("\nClosing Firefox browser session...") + driver.quit() + print("Firefox closed successfully.") diff --git a/submission_tanisha/Assignment 3.py b/submission_tanisha/Assignment 3.py new file mode 100644 index 0000000..b1953ec --- /dev/null +++ b/submission_tanisha/Assignment 3.py @@ -0,0 +1,171 @@ +""" +Assignment 3: CSS Selector Challenge - Wildcard attribute selectors +Site: https://rahulshettyacademy.com/AutomationPractice/ + +Goal: Locate elements whose id/attribute value is dynamic or shares a +common prefix/suffix, using CSS wildcard selectors instead of hardcoding +a full fixed id. + +CSS Wildcard Cheat Sheet: + [attr^='value'] -> attribute STARTS WITH value + [attr$='value'] -> attribute ENDS WITH value + [attr*='value'] -> attribute CONTAINS value anywhere +""" + +import sys +import time +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.firefox.options import Options as FirefoxOptions +from selenium.webdriver.firefox.service import Service as FirefoxService +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +# Optional fallback support for webdriver-manager if installed +try: + from webdriver_manager.firefox import GeckoDriverManager + WEBDRIVER_MANAGER_AVAILABLE = True +except ImportError: + WEBDRIVER_MANAGER_AVAILABLE = False + + +def initialize_firefox(): + """ + Initializes and returns a Firefox WebDriver instance. + Optimized for Arch Linux: + - Uses native geckodriver / Selenium Manager by default. + - Falls back to webdriver-manager if available. + """ + options = FirefoxOptions() + # options.add_argument("--headless") # Uncomment for headless test runs + + try: + # Selenium 4.6+ discovers geckodriver on Arch automatically + return webdriver.Firefox(options=options) + except Exception as primary_err: + if WEBDRIVER_MANAGER_AVAILABLE: + try: + service = FirefoxService(GeckoDriverManager().install()) + return webdriver.Firefox(service=service, options=options) + except Exception as fallback_err: + raise RuntimeError( + f"Failed to launch Firefox via webdriver-manager: {fallback_err}" + ) from primary_err + raise RuntimeError( + "Could not launch Firefox WebDriver. On Arch Linux, install geckodriver via:\n" + " sudo pacman -S firefox geckodriver\n" + f"Original error: {primary_err}" + ) + + +TARGET_URL = "https://rahulshettyacademy.com/AutomationPractice/" +driver = None + +try: + print("=" * 65) + print(" SELENIUM AUTOMATION: ASSIGNMENT 3 (CSS WILDCARD SELECTORS)") + print("=" * 65) + print("Launching Firefox browser on Arch Linux...") + + driver = initialize_firefox() + driver.maximize_window() + + print(f"Navigating to: {TARGET_URL}") + driver.get(TARGET_URL) + + # Initialize explicit wait + wait = WebDriverWait(driver, 10) + + # ========================================================================= + # 1. STARTS WITH: [id^='prefix'] + # Target: All checkboxes whose id starts with "checkBoxOption" + # Expected IDs: checkBoxOption1, checkBoxOption2, checkBoxOption3 + # ========================================================================= + print("\n" + "-" * 65) + print("1. CSS WILDCARD: STARTS WITH -> [id^='checkBoxOption']") + print("-" * 65) + + checkboxes = wait.until( + EC.presence_of_all_elements_located( + (By.CSS_SELECTOR, "[id^='checkBoxOption']") + ) + ) + print(f"Found {len(checkboxes)} checkboxes via [id^='checkBoxOption']:\n") + + for index, cb in enumerate(checkboxes, start=1): + cb.click() + cb_id = cb.get_attribute("id") + cb_val = cb.get_attribute("value") + print(f" {index}. Clicked checkbox id='{cb_id}' (value='{cb_val}') -> Checked: {cb.is_selected()}") + time.sleep(1) + + # ========================================================================= + # 2. CONTAINS: [id*='substring'] + # Target: Checkboxes containing 'BoxOption' anywhere in the ID + # ========================================================================= + print("\n" + "-" * 65) + print("2. CSS WILDCARD: CONTAINS -> [id*='BoxOption']") + print("-" * 65) + + contains_match = driver.find_elements(By.CSS_SELECTOR, "[id*='BoxOption']") + print(f"Found {len(contains_match)} elements matching contains selector [id*='BoxOption']") + for index, elem in enumerate(contains_match, start=1): + print(f" {index}. Tag: <{elem.tag_name}> | ID: '{elem.get_attribute('id')}'") + + # ========================================================================= + # 3. ENDS WITH: [id$='suffix'] + # Target: Select specifically the third checkbox ending with "Option3" + # ========================================================================= + print("\n" + "-" * 65) + print("3. CSS WILDCARD: ENDS WITH -> [id$='Option3']") + print("-" * 65) + + ends_with_match = driver.find_element(By.CSS_SELECTOR, "[id$='Option3']") + print(f"Target located: ID = '{ends_with_match.get_attribute('id')}' | Value = '{ends_with_match.get_attribute('value')}'") + + # ========================================================================= + # 4. WILDCARD ON NAME ATTRIBUTE: [name^='prefix'] + # Target: Radio buttons sharing name="radioButton" + # ========================================================================= + print("\n" + "-" * 65) + print("4. CSS WILDCARD ON NAME: [name^='radioButton']") + print("-" * 65) + + radio_buttons = driver.find_elements(By.CSS_SELECTOR, "[name^='radioButton']") + print(f"Found {len(radio_buttons)} radio buttons via [name^='radioButton']:\n") + + for index, rb in enumerate(radio_buttons, start=1): + rb_val = rb.get_attribute("value") + print(f" {index}. Radio button value='{rb_val}'") + + # Click the first radio button to demonstrate interaction + if radio_buttons: + radio_buttons[0].click() + print(f"\nAction: Selected first radio button (value='{radio_buttons[0].get_attribute('value')}').") + time.sleep(1) + + # ========================================================================= + # SUMMARY + # ========================================================================= + print("\n" + "=" * 65) + print("ASSIGNMENT 3 COMPLETED SUCCESSFULLY") + print("=" * 65) + print("Wildcard CSS Selectors Demonstrated:") + print(" 1. [id^='prefix'] -> Starts with (Targeted: checkBoxOption*)") + print(" 2. [id*='substring'] -> Contains (Targeted: *BoxOption*)") + print(" 3. [id$='suffix'] -> Ends with (Targeted: *Option3)") + print(" 4. [name^='prefix'] -> Starts with on name attribute") + print("=" * 65) + + # Keep browser open for inspection + input("\nPress ENTER in your terminal to close Firefox and exit...") + +except Exception as err: + print(f"\n[Execution Error]: {err}", file=sys.stderr) + +finally: + # Safely close Firefox session + if driver is not None: + print("\nClosing Firefox browser session...") + driver.quit() + print("Firefox closed successfully.") diff --git a/submission_tanisha/Assignment 4.py b/submission_tanisha/Assignment 4.py new file mode 100644 index 0000000..9e1440a --- /dev/null +++ b/submission_tanisha/Assignment 4.py @@ -0,0 +1,206 @@ +""" +Assignment 4: Child Nodes and Descendants Using CSS Selectors +Sites: + 1. https://rahulshettyacademy.com/AutomationPractice/ + 2. https://testautomationpractice.blogspot.com/ + +Goal: + Identify and locate child and nested web elements using CSS child combinators + and structural pseudo-classes. + +CSS Combinator & Pseudo-class Cheat Sheet: + parent > child -> DIRECT child only (one level down) + parent descendant -> Any descendant at any nesting depth (space) + parent > child:nth-child(n) -> Target the nth sibling element + parent > child:first-child -> Target the very first sibling + parent > child:last-child -> Target the last sibling +""" + +import sys +import time +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.firefox.options import Options as FirefoxOptions +from selenium.webdriver.firefox.service import Service as FirefoxService +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +# Optional fallback support for webdriver-manager if installed +try: + from webdriver_manager.firefox import GeckoDriverManager + WEBDRIVER_MANAGER_AVAILABLE = True +except ImportError: + WEBDRIVER_MANAGER_AVAILABLE = False + + +def initialize_firefox(): + """ + Initializes and returns a Firefox WebDriver instance. + Optimized for Arch Linux: + - Uses native geckodriver from system PATH / Selenium Manager. + - Falls back to webdriver-manager if available. + """ + options = FirefoxOptions() + # options.add_argument("--headless") # Uncomment for headless execution + + try: + # Selenium 4.6+ discovers geckodriver on Arch Linux automatically + return webdriver.Firefox(options=options) + except Exception as primary_err: + if WEBDRIVER_MANAGER_AVAILABLE: + try: + service = FirefoxService(GeckoDriverManager().install()) + return webdriver.Firefox(service=service, options=options) + except Exception as fallback_err: + raise RuntimeError( + f"Failed to launch Firefox via webdriver-manager: {fallback_err}" + ) from primary_err + raise RuntimeError( + "Could not launch Firefox WebDriver. On Arch Linux, install geckodriver via:\n" + " sudo pacman -S firefox geckodriver\n" + f"Original error: {primary_err}" + ) + + +def part_a_direct_child_and_descendants(driver, wait): + """ + Demonstrates: + 1. Direct Child Combinator: parent > child + 2. Descendant Combinator: parent descendant (space) + Target Site: Rahul Shetty Academy Practice Page + """ + target_url = "https://rahulshettyacademy.com/AutomationPractice/" + print("\n" + "=" * 65) + print("PART A: DIRECT CHILD & DESCENDANT COMBINATORS") + print("=" * 65) + print(f"Navigating to: {target_url}") + driver.get(target_url) + + # 1. DIRECT CHILD SELECTOR: fieldset > label + # Locates