Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update python locators docs #1903

Open
wants to merge 8 commits into
base: trunk
Choose a base branch
from

Conversation

pmartinez1
Copy link
Contributor

@pmartinez1 pmartinez1 commented Aug 27, 2024

User description

Thanks for contributing to the Selenium site and documentation!
A PR well described will help maintainers to review and merge it quickly

Before submitting your PR, please check our contributing guidelines.
Avoid large PRs, and help reviewers by making them as simple and short as possible.

Description

This PR moves the code to the test_locators.py file in the python/tests directory.

Motivation and Context

Helps the code be more organized, instead of having stand alone code that has not been tested.

Types of changes

  • [x ] Change to the site (I have double-checked the Netlify deployment, and my changes look good)
  • [x ] Code example added (and I also added the example to all translated languages)
  • Improved translation
  • Added new translation (and I also added a notice to each document missing translation)

Checklist

  • [x ] I have read the contributing document.
  • [x ] I have used hugo to render the site/docs locally and I am sure it works.

PR Type

Tests, Documentation


Description

  • Added comprehensive test cases for various Selenium locators in Python, including class name, CSS selector, ID, name, link text, partial link text, tag name, XPath, and relative locators.
  • Updated Python documentation across multiple languages to reference the new test file for locator examples, enhancing consistency and maintainability.

Changes walkthrough 📝

Relevant files
Tests
test_locators.py
Add test cases for various Selenium locators in Python     

examples/python/tests/elements/test_locators.py

  • Added multiple test functions for different locator strategies.
  • Included tests for class name, CSS selector, ID, name, link text,
    partial link text, tag name, and XPath.
  • Added a test for relative locators with a skip marker.
  • +84/-0   
    Documentation
    locators.en.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.en.md

  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.ja.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.ja.md

  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.pt-br.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.pt-br.md

  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 
    locators.zh-cn.md
    Update Python locator examples to reference test file       

    website_and_docs/content/documentation/webdriver/elements/locators.zh-cn.md

  • Updated Python code examples to reference new test file.
  • Replaced inline code with links to the test file.
  • +30/-40 

    💡 PR-Agent usage:
    Comment /help on the PR to get a list of all available PR-Agent tools and their descriptions

    Copy link

    netlify bot commented Aug 27, 2024

    Deploy Preview for selenium-dev ready!

    Name Link
    🔨 Latest commit 828f6ac
    🔍 Latest deploy log https://app.netlify.com/sites/selenium-dev/deploys/672d38420f765f0008e03259
    😎 Deploy Preview https://deploy-preview-1903--selenium-dev.netlify.app
    📱 Preview on mobile
    Toggle QR Code...

    QR Code

    Use your smartphone camera to open QR code link.

    To edit notification comments on pull requests, go to your Netlify site configuration.

    @codiumai-pr-agent-pro codiumai-pr-agent-pro bot added documentation Improvements or additions to documentation tests Review effort [1-5]: 2 labels Aug 27, 2024
    Copy link
    Contributor

    PR Reviewer Guide 🔍

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Key issues to review

    Resource Management
    The WebDriver instances are not properly closed using a context manager or try-finally block, which may lead to resource leaks.

    Test Isolation
    The tests are not properly isolated, as they share the same WebDriver instance across multiple test functions.

    Copy link
    Contributor

    codiumai-pr-agent-pro bot commented Aug 27, 2024

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Best practice
    Use a context manager for WebDriver to ensure proper resource cleanup

    Consider using a context manager (with statement) for the WebDriver to ensure proper
    cleanup of resources, even if an exception occurs.

    examples/python/tests/elements/test_locators.py [7-11]

    -driver = webdriver.Chrome()
    -driver.get("https://www.selenium.dev/")
    +with webdriver.Chrome() as driver:
    +    driver.get("https://www.selenium.dev/")
    +    driver.find_element(By.CLASS_NAME, "td-home")
     
    -driver.find_element(By.CLASS_NAME, "td-home")
    -
    -driver.quit()
    -
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    Why: Using a context manager for WebDriver is a best practice that ensures resources are properly cleaned up even if an exception occurs, improving code reliability and maintainability.

    9
    Improve code block formatting for consistency and readability

    Use consistent formatting for the Python code blocks across all examples. Add a
    blank line after the opening triple backticks and before the closing triple
    backticks to improve readability.

    website_and_docs/content/documentation/webdriver/elements/locators.en.md [84-85]

     {{< tab header="Python" text=true >}}
    +
     {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
     
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion improves readability by adding a blank line in the code block, which is a minor but beneficial enhancement for consistency across examples.

    7
    Enhancement
    Add an explicit wait before finding elements to improve test reliability

    Consider adding an explicit wait before finding elements to ensure the page has
    loaded completely, improving test reliability.

    examples/python/tests/elements/test_locators.py [7-11]

    +from selenium.webdriver.support.ui import WebDriverWait
    +from selenium.webdriver.support import expected_conditions as EC
    +
     driver = webdriver.Chrome()
     driver.get("https://www.selenium.dev/")
     
    -driver.find_element(By.CLASS_NAME, "td-home")
    +element = WebDriverWait(driver, 10).until(
    +    EC.presence_of_element_located((By.CLASS_NAME, "td-home"))
    +)
     
     driver.quit()
     
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    Why: Implementing an explicit wait ensures that elements are only searched for once they are present, which significantly improves the reliability of the tests by reducing flakiness due to timing issues.

    9
    Error handling
    Add error handling for potential exceptions during element location

    Add error handling to catch and handle potential exceptions that may occur during
    element location, such as NoSuchElementException.

    examples/python/tests/elements/test_locators.py [7-11]

    +from selenium.common.exceptions import NoSuchElementException
    +
     driver = webdriver.Chrome()
     driver.get("https://www.selenium.dev/")
     
    -driver.find_element(By.CLASS_NAME, "td-home")
    +try:
    +    driver.find_element(By.CLASS_NAME, "td-home")
    +except NoSuchElementException:
    +    print("Element not found")
    +finally:
    +    driver.quit()
     
    -driver.quit()
    -
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: Adding error handling for exceptions like NoSuchElementException enhances the robustness of the test by allowing graceful handling of errors, which is crucial for reliable test execution.

    8
    Consistency
    ✅ Synchronize Python code examples across different language versions of the documentation
    Suggestion Impact:The Python code examples in the Japanese documentation were updated to match the English version by adjusting line numbers.

    code diff:

       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -111,7 +111,7 @@
     	driver.findElement(By.cssSelector("#fname"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L18" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L17" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -141,7 +141,7 @@
     	driver.findElement(By.id("lname"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L26" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L25" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -172,7 +172,7 @@
     	driver.findElement(By.name("newsletter"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L34" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L33" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -201,7 +201,7 @@
     	driver.findElement(By.linkText("Selenium Official Page"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L42" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L41" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -231,7 +231,7 @@
     	driver.findElement(By.partialLinkText("Official Page"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L50" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L49" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -259,7 +259,7 @@
     	driver.findElement(By.tagName("a"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L58" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L57" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -293,7 +293,7 @@
     	driver.findElement(By.xpath("//input[@value='f']"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L66" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L65" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -318,6 +318,8 @@
     all you need to do is utilize `webdriver.common.by.By`, however in 
     others it's as simple as setting a parameter in the FindElement function
     
    +### By
    +
     {{< tabpane langEqualsHeader=true >}}
     {{< badge-examples >}}
       {{< tab header="Java" >}}
    @@ -326,7 +328,7 @@
     	driver.findElement(By.className("information"));
       {{< /tab >}}
       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}}
       {{< /tab >}}
       {{< tab header="CSharp" >}}
         var driver = new ChromeDriver();
    @@ -346,7 +348,59 @@
       {{< /tab >}}
     {{< /tabpane >}} 
     
    -
    +### ByChained
    +
    +The `ByChained` class enables you to _chain_ two By locators together. For example, instead of having to 
    +locate a parent element, and then a child element of that parent, you can instead combine those two `FindElement` 
    +functions into one.
    +
    +{{< tabpane langEqualsHeader=true >}}
    +  {{< tab header="Java" text=true >}}
    +    {{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java#L37-L38" >}}
    +  {{< /tab >}}
    +  {{< tab header="Python" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="CSharp" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="Ruby" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="JavaScript" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="Kotlin" text=true >}}
    +    {{< badge-code >}}
    +  {{< /tab >}}
    +{{< /tabpane >}} 
    +
    +### ByAll
    +
    +The `ByAll` class enables you to utilize two By locators at once, finding elements that mach _either_ of your By locators. 
    +For example, instead of having to utilize two `FindElement()` functions to find the username and password input fields seperately, 
    +you can instead find them together in one clean `FindElements()`
    +
    +{{< tabpane langEqualsHeader=true >}}
    +  {{< tab header="Java" text=true >}}
    +    {{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/elements/LocatorsTest.java#L22-L23">}}
    +  {{< /tab >}}
    +  {{< tab header="Python" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="CSharp" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="Ruby" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="JavaScript" text=true >}}
    +  {{< badge-code >}}
    +  {{< /tab >}}
    +  {{< tab header="Kotlin" text=true >}}
    +    {{< badge-code >}}
    +  {{< /tab >}}
    +{{< /tabpane >}} 
     
     ## 相対ロケーター
     
    @@ -382,7 +436,7 @@
     By emailLocator = RelativeLocator.with(By.tagName("input")).above(By.id("password"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L75" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L73" >}}
     {{< /tab >}}
     {{< tab header="CSharp" >}}
     var emailLocator = RelativeBy.WithLocator(By.TagName("input")).Above(By.Id("password"));
    @@ -408,7 +462,7 @@
     By passwordLocator = RelativeLocator.with(By.tagName("input")).below(By.id("email"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L77" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L75" >}}
     {{< /tab >}}
     {{< tab header="CSharp" >}}
     var passwordLocator = RelativeBy.WithLocator(By.TagName("input")).Below(By.Id("email"));
    @@ -434,7 +488,7 @@
     By cancelLocator = RelativeLocator.with(By.tagName("button")).toLeftOf(By.id("submit"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L79" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L77" >}}
     {{< /tab >}}
     {{< tab header="CSharp" >}}
     var cancelLocator = RelativeBy.WithLocator(By.tagName("button")).LeftOf(By.Id("submit"));
    @@ -460,7 +514,7 @@
     By submitLocator = RelativeLocator.with(By.tagName("button")).toRightOf(By.id("cancel"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L81" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L79" >}}
     {{< /tab >}}
     {{< tab header="CSharp" >}}
     var submitLocator = RelativeBy.WithLocator(By.tagName("button")).RightOf(By.Id("cancel"));
    @@ -488,7 +542,7 @@
     By emailLocator = RelativeLocator.with(By.tagName("input")).near(By.id("lbl-email"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L83" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L81" >}}
     {{< /tab >}}
     {{< tab header="CSharp" >}}
     var emailLocator = RelativeBy.WithLocator(By.tagName("input")).Near(By.Id("lbl-email"));
    @@ -513,7 +567,7 @@
     By submitLocator = RelativeLocator.with(By.tagName("button")).below(By.id("email")).toRightOf(By.id("cancel"));
     {{< /tab >}}
     {{< tab header="Python" text=true >}}
    -{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L85" >}}
    +{{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L83" >}}

    Ensure that the Python code examples are consistent across all language versions of
    the documentation. Update the Japanese version to match the changes made in the
    English version.

    website_and_docs/content/documentation/webdriver/elements/locators.ja.md [81-82]

     {{< tab header="Python" text=true >}}
    +
     {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
     
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: Ensuring consistency across language versions is important for maintaining uniformity in documentation, although it is a minor change.

    7
    ✅ Align Python code examples in the Portuguese-Brazilian documentation with the English version
    Suggestion Impact:The commit updated the line numbers in the Portuguese-Brazilian documentation to align with the English version, ensuring consistency across language versions.

    code diff:

       {{< tab header="Python" text=true >}}
    -  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
    +  {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L9" >}}

    Update the Portuguese-Brazilian version of the documentation to match the changes
    made in the English version, ensuring consistency across all language versions.

    website_and_docs/content/documentation/webdriver/elements/locators.pt-br.md [84-85]

     {{< tab header="Python" text=true >}}
    +
     {{< gh-codeblock path="examples/python/tests/elements/test_locators.py#L10" >}}
     
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion promotes consistency across different language versions, which is beneficial for users accessing the documentation in various languages, though it is a minor change.

    7
    Maintainability
    Parameterize test functions to reduce code duplication and improve maintainability

    Consider parameterizing the test functions to reduce code duplication and make it
    easier to add new test cases for different locator types.

    examples/python/tests/elements/test_locators.py [6-19]

    -def test_find_by_classname():
    -    driver = webdriver.Chrome()
    -    driver.get("https://www.selenium.dev/")
    +@pytest.mark.parametrize("locator, value", [
    +    (By.CLASS_NAME, "td-home"),
    +    (By.CSS_SELECTOR, "#announcement-banner"),
    +    # Add more locator types and values here
    +])
    +def test_find_element(locator, value):
    +    with webdriver.Chrome() as driver:
    +        driver.get("https://www.selenium.dev/")
    +        driver.find_element(locator, value)
     
    -    driver.find_element(By.CLASS_NAME, "td-home")
    -
    -    driver.quit()
    -
    -def test_find_by_css_selector():
    -    driver = webdriver.Chrome()
    -    driver.get("https://www.selenium.dev/")
    -
    -    driver.find_element(By.CSS_SELECTOR, "#announcement-banner")
    -
    -    driver.quit()
    -
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: Parameterizing test functions reduces code duplication and enhances maintainability, making it easier to add new test cases and manage existing ones, although it is not as critical as resource management or error handling.

    7

    @pmartinez1
    Copy link
    Contributor Author

    Could I get a review please?

    Copy link
    Contributor

    @shbenzer shbenzer left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Thank you for the contribution @pmartinez1 ! A few changes must be made before it can be properly reviewed:

    pytest import is unnecessary, pytest will not run a function if it is not prefixed by test unless explicitly called in a test function. Line 70 can be removed as well

    @pmartinez1
    Copy link
    Contributor Author

    Thank you for the contribution @pmartinez1 ! A few changes must be made before it can be properly reviewed:

    pytest import is unnecessary, pytest will not run a function if it is not prefixed by test unless explicitly called in a test function. Line 70 can be removed as well

    Thanks for pointing that out. I've updated the PR.

    @shbenzer
    Copy link
    Contributor

    shbenzer commented Sep 24, 2024

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest.

    Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)?

    Once that's done I'll test it locally

    @pmartinez1
    Copy link
    Contributor Author

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest.

    Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)?

    Once that's done I'll test it locally

    No problem. And yes, will split them up.

    @pmartinez1
    Copy link
    Contributor Author

    Sorry about that, honestly forgot the relative locator section was included in this pr. I only did a cursory look through this commit's changes and commented when I saw you added an import for pytest.
    Could you expand the relative locator section to have an individual test for each relative locator (above, below, rightof, leftof, and near)?
    Once that's done I'll test it locally

    No problem. And yes, will split them up.

    I ended updating them a bit more. Originally, I had created the test not to run, but follow along with the static image that is present, but I think your approach is better. I borrowed a little from your PR (for the sake of expediency) and now the tests are ready to be run.

    @shbenzer
    Copy link
    Contributor

    Use what's available, I don't mind. I'll pull the branch for test

    @shbenzer
    Copy link
    Contributor

    shbenzer commented Sep 24, 2024

    Tested changes locally and looks good. The goal is to move all the code examples to executable files in the repo, so eventually this whole section on relative locators will eventually be refactored to no longer refer to that image.

    What do you think about this PR? @harsha509

    @pmartinez1
    Copy link
    Contributor Author

    Hi @harsha509,

    Would you be able to give this a review? @shbenzer has already given it one pass.

    Thank you.

    Copy link
    Member

    @harsha509 harsha509 left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Thank you @pmartinez1 !

    @pmartinez1
    Copy link
    Contributor Author

    Wow. Again tests are failing on my PR :(

    For some reason, the "test_find_by_link_text" test is failing on Windows only, but seems to be deterministic. Does anyone have a Windows machine they can test on? Any ideas? I can't think of a reason why it fails there or at all. The test is simply going to the Selenium and looking for "Documentation". It passes in every other environment and a similar test, "test_find_by_partial_link_text" passes with no issue everywhere.

    I could add waits to see if it is a timing/loading issue?

    I haven't gotten around to finding an option for testing Windows yet, but it is on my list.
    Screenshot 2024-10-04 at 11 40 42 AM

    Copy link

    @A1exKH A1exKH left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    @pmartinez1 Code in your PR LGTM.
    Please investigate and fix failed tests.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    documentation Improvements or additions to documentation Review effort [1-5]: 2 tests
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    5 participants